c++ 在派生函数中启动 OMP 线程

c++ Start an OMP thread in a derived function

本文关键字:启动 OMP 线程 函数 派生 c++      更新时间:2023-10-16

假设我有一个基类,其中包含一个带有两个OMP线程的虚函数:

class Parent {
public:
Parent() {}
~Parent() {}
virtual void f() {
#pragma omp parallel sections
{
#pragma omp section
{
// do_something_1();
}
#pragma omp section
{
// do_something_2();
}
}
}
}

然后我有一个这样的派生类:

class Child : public Parent {
public:
Child() {}
~Child() {}
void f() {
Parent::f();
// Other thread OMD
}
}

我想最后让来自父类的两个线程和来自 Child 的线程运行,但它不起作用。这种设计甚至可能吗?

问题是 OpenMP 指令位于父级的虚拟函数内,因此 在派生类中不可见。在派生类中调用父代码时 这提出了两种可能的解决方案,都有优点和缺点。

版本 1 将父级的操作保密,但只能额外扩展一次 水平。

class Parent {
public:
void f() {
#pragma omp parallel sections
{
#pragma omp section
{
// do_something_1();
}
#pragma omp section
{
// do_something_2();
}
#pragma omp section
{
this->f_impl();
}
}
}
private:
virtual void f_impl() {}; // do nothing placeholder
}
class Child : public Parent {
private:
void f_impl() override;
}

版本 2 可以无限期扩展,但需要公开每个父级的内部结构。

class Parent {
public:
virtual void f() {
#pragma omp parallel sections
{
#pragma omp section
{
f_impl1();
}
#pragma omp section
{
f_impl2();
}
}
}
protected:
void f_impl1();
void f_impl2();
}
class Child : public Parent {
public:
virtual void f() {
#pragma omp parallel sections
{
#pragma omp section
{
f_impl1();
}
#pragma omp section
{
f_impl2();
}
#pragma omp section
{
f_impl3();
}
}
}
protected:
void f_impl3();
}
class Child2 : public Child {
public:
virtual void f() {
#pragma omp parallel sections
{
#pragma omp section
{
f_impl1();
}
#pragma omp section
{
f_impl2();
}
#pragma omp section
{
f_impl3();
}
#pragma omp section
{
f_impl4();
}
}
}
protected:
void f_impl4();
}

在这种情况下,我宁愿建议使用任务,并确保所有内容都在并行上下文中执行,例如:

// prepare the parallel context somewhere outside
#pragma omp parallel
#pargma omp single
run();

class Parent {
public:
Parent() {}
~Parent() {}
virtual void f() {
#pragma omp task
{
// do_something_1();
}
// do_something_2();
#pragma omp taskwait
// It's not nice for reasoning about code to keep tasks running here
}
}
class Child : public Parent {
public:
Child() {}
~Child() {}
void f() {
// swap the order in the code
// to account for taskwait at the end of paraent::f
#pragma omp task
{
// Other thread OMD
}
Parent::f();
// if you feel like it, you can also add a taskwait here
}
}

如您所见,如果在应用程序中确保并行上下文(但使用代码的单一执行),则可以轻松地使用任务来实现派生调用之间的并行化。但是,请注意在函数调用结束时保留执行任务,因为这可能很危险。确保调用方知道何时必须taskwait以确保操作完成 - 或者交换它,以便调用方并行执行任务与被调用方执行的任何操作。

如果没有线程团队,也可以按需创建并行上下文,但我认为这不那么干净且更危险。