【问题标题】:c++ Start an OMP thread in a derived functionc++ 在派生函数中启动 OMP 线程
【发布时间】:2017-11-24 19:53:13
【问题描述】:

假设我有一个带有两个 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
  }
}

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

【问题讨论】:

    标签: c++ multithreading openmp


    【解决方案1】:

    问题是 OpenMP 指令在 Parent 的虚函数中,所以 在您的派生类中看不到。当您在派生类中调用父代码时 这提出了两种可能的解决方案,各有优缺点。

    版本 1 将 Parent 的操作保密,但只能额外扩展一个 级别。

    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 可以无限扩展,但需要公开每个 Parent 的内部结构。

    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();
    }
    

    【讨论】:

    • 谢谢,我选择了选项 1,效果很好!
    • 您还可以根据@Zulan 的答案和选项1 使用任务。这使得它更加灵活,因为您可以将#pragma omp task 移动到派生函数中,从而允许您多次调用#pragma omp task你的派生函数。
    【解决方案2】:

    在这种情况下,我宁愿推荐使用任务,并确保一切都在并行上下文中执行,例如:

    // 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 以确保操作完成 - 或交换它以便调用者执行与被调用者并行执行的任务。

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

    【讨论】:

      猜你喜欢
      • 2017-09-04
      • 2019-04-19
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 2023-01-10
      • 1970-01-01
      • 2020-07-23
      • 1970-01-01
      相关资源
      最近更新 更多