2011-09-25 15 views
7

Siguiendo this excellent tutorial para futuros , promesas y tareas envasados ​​ llegué al punto donde quería preparar mi propia tarea¿Cómo creo una packaged_task con parámetros?

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

Por lo que yo puedo descifrar el gcc-4.7.0 mensaje de error espera los argumentos de manera diferente? ¿Pero cómo? Trato de acortar el mensaje de error:

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' 

¿Es mi variante de la forma que proporcione los parámetros para ackermann mal? ¿O es el parámetro de plantilla incorrecto? No doy los parámetros 3,11 a la creación de hilo, ¿verdad?

Actualizar otras variantes sin éxito:

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

hmm ... ¿soy yo, o es el beta-gcc?

Respuesta

16

En primer lugar, si declara std::packaged_task para tomar argumentos, debe pasarlos al operator(), no al constructor. En un solo hilo se puede por lo tanto hacer:

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

a hacer lo mismo con un hilo, debe movimiento la tarea en el hilo y pasar los argumentos también:

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; 

Alternativamente, puede enlazar directamente los argumentos antes de construir la tarea, en cuyo caso la tarea en sí tiene ahora una firma que no tiene argumentos:

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

Aga , puede hacer esto y pasarlo a un hilo:

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; 

Todos estos ejemplos deben trabajar (y lo hacen, tanto con g ++ 4.6 y MSVC2010 y mi just::thread implementación de la biblioteca de subprocesos). Si alguno no lo hace, entonces hay un error en el compilador o biblioteca que está utilizando. Por ejemplo, la biblioteca se envía con g ++ 4.6 no puede gestionar pasar objetos de solo movimiento como std::packaged_task a std::thread (y por lo tanto no puede manejar los ejemplos 2 y 4), ya que usa std::bind como un detalle de implementación, y esa implementación de std::bind requiere incorrectamente que los argumentos sean reproducibles.

+3

En cuanto a los requisitos para 'std :: bind', no es cierto que se requiera que los argumentos sean copiables. El párrafo es demasiado largo para pegarlo aquí, es 20.8.9.1.2 Enlace de plantilla de función [func.bind.bind] párrafo 5. Los requisitos actuales son que los tipos almacenados se muevan de forma constructiva y construible a partir de los argumentos pasados. Aunque sí recuerdo un envío de implementación defectuoso con GCC que requería CopyConstructible. (Sin embargo, no es el problema que enfrenta el OP.) –

+0

@Luc: tienes razón, lo he olvidado. –

+0

El código funciona, pero tengo una pregunta en la línea 'std :: unique_lock l (td.m);' ¿no es el mutex en el hilo ya bloqueado cuando está comprobando la variable de condición? Creo que me falta algo allí. Pensé que bloquea el mutex en el hilo y luego espera a que la bandera de parada o el trabajo se agregue a la cola. En este caso, ¿el hilo principal no esperaría a que se liberara el bloqueo? ¿Es esto debido a un falso despertar que esto funciona? – bjackfly

3

Dado que está iniciando el subproceso sin argumentos, espera que la tarea se inicie sin argumentos, como si se usara task1(). Por lo tanto, la firma que desea admitir no es int(int, int) sino int(). A su vez, esto significa que debe pasar un functor que sea compatible con esta firma al constructor de std::packaged_task<int()>. Proveedores:

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

Otra posibilidad es:

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

porque el constructor de std::thread puede aceptar los argumentos. Aquí, el funtor que pasa se usará como si se usara task1(3, 11).

+0

Alas, no. Tal vez intento un functor para 'ackermann' – towi

+0

@towi 'No' a qué? ¿Un error de compilación nuevamente? ¿Cuál es el error? –

+0

Sí, diferentes errores. Creo que lo más cerca que conseguí (la lista de errores más corta) fue: 'packaged_task task1 (bind (ack, 3,11)); thread th1 {move (task1)}; 'con error:' tuple: 274: 17: error: 'constexpr std :: _ Tuple_impl <### = std :: _ Tuple_impl <0ul, std :: packaged_task >]' declarado a tomar referencia const, pero la declaración implícita tomaría non-const' – towi

Cuestiones relacionadas