5

私はワーク スチール アルゴリズムを実装しており、ラッパー テンプレートへの可変引数の 1 つとしてプロミスを取る汎用関数ラッパーを作成しています。これらの関数ラッパーを使用してタスクを作成し、プロミスを使用して各ノードが依存ノードと通信できるようにしたいと考えています。各ノードは、依存ノードと約束/先物のリストを維持します。各ノードは、すべての先物が設定されているかどうかを確認することで実行できます。プロミスは、関数ラッパーがさまざまなオブジェクトを返すジョブによって異なります。1 つのアルゴリズムを、メッセージの読み取りとメッセージのデコード、オブジェクトのチェックの実行、すべてのチェックの結果の返しなどの個別の操作に分割できる場合、これらの各アクションは異なる promise (オブジェクト、ブール値、結果) を返します。

ただし、書籍C++ Concurrency in Actionには関数ラッパーの実装がありますが、このユース ケースは処理されません。オンラインの他の参考文献では、 std::promise のようなプロミスへのハードコーディングされた参照を見てきましたが、これは 1 つのタイプのみです。

誰かが次のことを達成するためにラッパーを書く方法をアドバイスできますか...

void add(int a, int b, std::promise<int>&& prms)
{
   int res = a + b;
   try {
      prms.set_value(res);
   }
   catch(...)
   {
      prms.set_exception(std::current_exception());
   }
}

int main()
{
   std::promise<int> prms;
   std::future<int> fut = prms.get_future();
   FunctionWrapper myFunctor(a, 10, 20, std::move(prms));

   // add the functor to the queue and it will be retrieved by a thread
   // that executes the task. since i have the future, i can pass it to the 
   // dependent worknode
}

以下のようなコードを書いてみました...しかし、これを機能させるのは困難でした。

#ifndef FUNCTIONWRAPPER_HPP
#define FUNCTIONWRAPPER_HPP

template<typename F, typename R, typename... Args>
class FunctionWrapper
{
  class implbase
  {
  public:
    virtual ~implbase();
    virtual R execute(Args...)=0;
  };

  class impl : public implbase
  {
  public:
    impl(F&& f) : func(std::move(f)) {}
    virtual R execute(Args... args) { return func(args...); }

  private:
    F func;
  };

  std::shared_ptr<impl> internalFunc;

public:
  FunctionWrapper(F&& f) : internalFunc(0)
  {
    internalFunc = new impl<F, R, Args...>(f);
  }

  FunctionWrapper(const FunctionWrapper& other)
    : internalFunc(std::move(other.internalFunc))
  {}

  ~FunctionWrapper()
  {
    if(internalFunc)
      delete internalFunc;
  }

  R operator()(Args... args)
  {
    return internalFunc->execute(args...);
  }

  void swap(FunctionWrapper& other)
  {
    impl<R, Args...>* tmp = internalFunc;
    internalFunc = other.internalFunc;
    other.internalFunc = tmp;
  }

  FunctionWrapper& operator=(const FunctionWrapper& other)
  {
    FunctionWrapper(other).swap(*this);
    return *this;
  }

  FunctionWrapper& operator=(const F& f)
  {
    FunctionWrapper(f).swap(*this);
    return *this;
  }
};

#endif // FUNCTIONWRAPPER_HPP
4

1 に答える 1

8

C++11 には、これを行うためのラッパーがあります。といいpackaged_taskます。

呼び出し可能なオブジェクト (関数オブジェクト、ラムダ、関数ポインター、バインド式など) をラップし、get_future()渡された関数の戻り値の型に一致するメソッドを介して Future を提供します。

次の例を検討してください。

#include <thread>
#include <future>
#include <functional>
#include <iostream>

using namespace std;

int add(int a, int b)
{
    return a + b;
}

int main()
{
    // Create a std::packaged_task and grab the future out of it.
    packaged_task<int()> myTask(bind(add, 10, 20));
    future<int> myFuture = myTask.get_future();

    // Here, is where you would queue up the task in your example.
    // I'll launch it on another thread just to demonstrate how.
    thread myThread(std::move(myTask));
    myThread.detach();

    // myFuture.get() will block until the task completes.
    // ...or throw if the task throws an exception.
    cout << "The result is: " << myFuture.get() << endl;
    return 0;
}

ご覧のとおり、 を渡すのではなく、promisepackaged_task約束を作成し、未来を提供し​​てくれることを期待しています。

また、バインド式を使用することで、タスクに引数を効果的に渡して、呼び出されるまで保持することができます。

を使用packaged_taskすると、将来にわたって例外をプッシュする負担が の手に委ねられpackaged_taskます。そうすれば、関数は を呼び出す必要がなくなりますset_exception()。彼らは返すか投げるだけです。

于 2012-12-22T06:12:04.183 に答える