9

按照这个关于期货承诺打包任务的优秀教程,我到了想要准备自己的任务的地步

#include <iostream>
#include <future>
using namespace std;

int ackermann(int m, int n) {   // might take a while
    if(m==0) return n+1;
    if(n==0) return ackermann(m-1,1);
    return ackermann(m-1, ackermann(m, n-1));
}

int main () {
    packaged_task<int(int,int)> task1 { &ackermann, 3, 11 }; // <- error
    auto f1 = task1.get_future();
    thread th1 { move(task1) };                              // call
    cout << "  ack(3,11):" << f1.get() << endl;
    th1.join();
}

据我可以破译 gcc-4.7.0 错误消息,它期望的参数不同吗?但是怎么做?我尝试缩短错误消息:

error: no matching function for call to 
  'std::packaged_task<int(int, int)>::packaged_task(<brace-enclosed initializer list>)'
note: candidates are:
  std::packaged_task<_Res(_ArgTypes ...)>::---<_Res(_ArgTypes ...)>&&) ---
note:   candidate expects 1 argument, 3 provided
  ...
note:   cannot convert 'ackermann'
  (type 'int (*)(int, int)') to type 'std::allocator_arg_t'

我的变体是如何提供错误的参数ackermann吗?还是模板参数错误?我不给3,11创建线程的参数吧?

更新其他不成功的变体:

packaged_task<int()> task1 ( []{return ackermann(3,11);} );
thread th1 { move(task1)  };

packaged_task<int()> task1 ( bind(&ackermann,3,11) );
thread th1 { move(task1)  };

packaged_task<int(int,int)> task1 ( &ackermann );
thread th1 { move(task1), 3,11  };

嗯……是我,还是 beta-gcc?

4

2 回答 2

21

首先,如果您声明std::packaged_task接受参数,那么您必须将它们传递给operator(),而不是构造函数。因此,您可以在单个线程中执行以下操作:

std::packaged_task<int(int,int)> task(&ackermann);
auto f=task.get_future();
task(3,11);
std::cout<<f.get()<<std::endl;

要对线程执行相同的操作,您必须任务移动到线程中,并传递参数:

std::packaged_task<int(int,int)> task(&ackermann);
auto f=task.get_future();
std::thread t(std::move(task),3,11);
t.join();
std::cout<<f.get()<<std::endl;

或者,您可以在构造任务之前直接绑定参数,在这种情况下,任务本身现在有一个不带参数的签名:

std::packaged_task<int()> task(std::bind(&ackermann,3,11));
auto f=task.get_future();
task();
std::cout<<f.get()<<std::endl;

同样,您可以这样做并将其传递给线程:

std::packaged_task<int()> task(std::bind(&ackermann,3,11));
auto f=task.get_future();
std::thread t(std::move(task));
t.join();
std::cout<<f.get()<<std::endl;

所有这些示例都应该可以工作(并且可以使用 g++ 4.6 和 MSVC2010 以及我的线程库的just::thread实现)。如果没有,那么您正在使用的编译器或库中存在错误。例如,g++ 4.6 附带的库无法处理传递只移动对象,例如 a std::packaged_taskto std::thread(因此无法处理第二个和第四个示例),因为它std::bind用作实现细节,并且std::bind错误的实现需要参数是可复制的。

于 2011-09-25T21:58:06.513 回答
3

由于您在没有参数的情况下启动线程,因此您希望任务在没有参数的情况下启动,就像task1()使用过一样。因此,您要支持的签名不是int(int, int)but int()。反过来,这意味着您必须将与此签名兼容的仿函数传递给std::packaged_task<int()>. 尝试:

packaged_task<int()> task1 { std::bind(&ackermann, 3, 11) };

另一种可能是:

packaged_task<int(int,int)> task1 { &ackermann };
auto f1 = task1.get_future();
thread th1 { move(task1), 3, 11 };

因为 的构造函数std::thread可以接受参数。在这里,你传递给它的函子将被使用,就像task1(3, 11)被使用一样。

于 2011-09-25T20:53:48.963 回答