4

1つのスレッドで実行する必要があり、多くのスレッドでフィードできる関数のワークキューを作成しようとしています。これを実現するために、boost::packaged_taskとboost::unique_futureを使用することを計画していました。アイデアはあなたがするだろう:

Foo value = queue.add(myFunc).get();

関数が実行されるまで、これはブロックされます。したがって、queue.add(...)はboost :: functionを受け取り、boost::unique_futureを返します。次に、内部的に、コンストラクターにboost::functionを使用してboost::packaged_taskを作成します。

私が遭遇している問題は、boost ::function<...>が毎回同じになるとは限らないということです。具体的には、その戻り値が変更されます(ただし、関数がパラメーターを受け取ることはありません)。したがって、次のような追加関数が必要です。

template <typename ResultType>
boost::unique_future<ResultType> add(boost::function<ResultType ()> f) {
   boost::packaged_task<boost::function<ResultType ()> > task(f);
   queue.push_back(task);
   return task.get_future();
}

さて、それはそれほど悪くはないようですが、それから私は「キュー」を定義する方法の問題に遭遇しました。タイプは一定ではないので、boost::anyを使用する以外に選択肢はないと思います。

std::list<boost::any> queue; // note: I'm not concerned with thread-safety yet

しかし、executeSingleを実装しようとすると問題が発生します(実行するためにキューから1つのアイテムだけを取り出します)。

void executeSingle() {
    boost::any value = queue.back();
    boost::packaged_task<?> task = boost::packaged_task<?>(boost::move(value));
    // actually execute task
    task();
    queue.pop_back();
}

'?' 私がよくわからないことを示してください。別のスレッドから呼び出されるため、テンプレートを使用してexecuteSingleを呼び出すことはできません。boost :: anyを使用しようとしましたが、エラーが発生します:

  conversion from 'boost::any' to non-scalar type  boost::detail::thread_move_t<boost:thread>' requested.

面白いのは、この時点では実際にはpackaged_taskの戻りタイプは気にせず、実行したいだけですが、テンプレートの詳細は理解できます。

どんな洞察も大歓迎です!

4

3 に答える 3

5

を保存する必要がありboost::function<void()>ます。boost::packaged_task<R>::operator()何も返さないことに注意してください。関連付けられたにデータを入力しますboost::future。実際、boost::function<void()>戻り値に関心がないため、使用できるものが返されたとしても、気になるのはを呼び出すことだけqueue.back()()です。この場合boost::function<void()>::operator()、戻り値を破棄するように処理されます。

ちょっとした注意として、メソッドのシグネチャを変更して、ではなくaddジェネリック型でテンプレート化することをお勧めします。これを使用して、の結果型を取得します。Functorboost::functionboost::result_ofboost::packaged_task

全体としての私の提案:

template<typename Functor>
boost::future<typename boost::result_of<Functor()>::type>
queue::add(Functor functor) // assuming your class is named queue
{
    typedef typename boost::result_of<Functor()>::type result_type;
    boost::packaged_task<result_type> task(functor);
    boost::unique_future<result_type> future = task.get_future();
    internal_queue.push_back(boost::move(task)); // assuming internal_queue member
    return boost::move(future);
}

void
queue::executeSingle()
{
    // Note: do you really want LIFO here?
    queue.back()();
    queue.pop_back();
}

編集

内部の移動セマンティクスの処理方法queue::add

typedef typename boost::result_of<Functor()>::type result_type;
typedef boost::packaged_task<result_type> task_type;
boost::shared_ptr<task_type> task = boost::make_shared<task_type>(functor);
boost::unique_future<result_type> future = task->get_future();

/* boost::shared_ptr is possibly move-enabled so you can try moving it */
internal_queue.push_back( boost::bind(dereference_functor(), task) );

return boost::move(future);

どこにあるdereference_functor可能性があります:

struct dereference_functor {
    template<typename Pointer>
    void
    operator()(Pointer const& p) const
    {
        (*p)();
    }
};

bindより明確な表現に置き換えることもできます

boost::bind(&task_type::operator(), task)

これもカスタムファンクターを必要としません。ただし、これに複数の過負荷がある場合は、task_type::operator()曖昧性解消が必要になる場合があります。Boost.Threadの将来の変更により過負荷が発生した場合にも、コードが破損する可能性があります。

于 2011-05-05T04:05:48.023 に答える
1

昔ながらの仮想関数を使用します。メソッドを使用して基本クラスtask_basevirtual execute定義してから、特定のタスクインスタンスを保持するテンプレート派生クラスを定義します。線に沿った何か:

struct task_base {
  virtual void execute() = 0;
};
template<typename ResultType>
struct task_holder : task_base {
  task_holder(boost::packaged_task<boost::function<ResultType ()> >&& task)
    : m_task(task) { }
  void execute() {
    m_task();
  }
private:
  boost::packaged_task<boost::function<ResultType ()> > m_task;
};

そして、保持するキューを定義しますunique_ptr<task_base>。これは基本的に何をするかboost::anyであり、特定の関数、つまりを使用するのはあなただけですexecute

注:テストされていないコード!そして、私はまだ右辺値参照にあまり精通していません。これは、コードがどのように見えるかを理解するためのものです。

于 2011-05-04T21:06:57.737 に答える
0

やや遅れていますが、独自のキューランナーソリューションをロールする代わりにBoost.Asioの使用を検討することをお勧めします。

これはI/Oライブラリとして成長しましたが、このように非同期呼び出しをサポートします。どこかを定義し、io_serviceそれをスレッド内で実行してから、postそのスレッドで呼び出されるファンクターを作成するだけです。

于 2013-09-06T01:14:33.460 に答える