2011-09-25 9 views
7

Po this excellent tutorial dla futures, obietnic i pakietach zadań dotarłem do punktu, w którym chciałem przygotować własne zadanieJak utworzyć parametr packaged_task z parametrami?

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

O ile mogę rozszyfrować gcc-4.7.0 komunikat o błędzie inaczej oczekuje argumentów? Ale jak? Staram się skrócić komunikat o błędzie:

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' 

jest mój wariant jaki sposób zapewnić parametry ackermann złym? Czy jest to zły parametr szablonu? Nie podaję parametrów 3,11 do tworzenia wątku, prawda?

Aktualizuj inne nieudane warianty:

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 ... czy to ja, czy jest to beta-gcc?

Odpowiedz

16

Po pierwsze, jeśli zadeklarujesz std::packaged_task do podjęcia argumentów, musisz przekazać je do operator(), a nie do konstruktora. W jednym wątku można zatem zrobić:

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

Aby zrobić to samo z wątku, musisz ruch zadanie w gwint, i przekazać argumenty za:

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; 

Alternatywnie, można powiązać argumenty bezpośrednio przed konstruować zadania, w tym przypadku sama zadanie ma teraz podpis, który nie bierze argumenty:

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

Aga w, można to zrobić i przekazać je do wątku:

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; 

Wszystkie te przykłady powinny działać (i zrobić z obu g ++ 4.6 i MSVC2010 i mojego just::thread realizacji biblioteki wątku). Jeśli tak się nie stanie, oznacza to błąd w kompilatorze lub bibliotece, z której korzystasz. Na przykład biblioteka jest dostarczana z g ++ 4.6 nie może obsłużyć przechodzących obiektów tylko ruchowych, takich jak std::packaged_task do std::thread (i tym samym nie obsługuje 2 i 4 przykładów), ponieważ używa std::bind jako szczegółów implementacji, a implementacja std::bind niepoprawnie wymaga, aby argumenty były kopiowalne.

+3

Patrząc na wymagania dla 'std :: bind', nie jest prawdą, że argumenty są wymagane do kopiowania. Paragraf jest zbyt długi, aby go tu wkleić, to jest 20.8.9.1.2 Powiązanie szablonu funkcji [func.bind.bind] paragraf 5. Rzeczywiste wymagania są takie, że przechowywane typy są ruchome i możliwe do skonstruowania na podstawie przekazanych argumentów. Chociaż pamiętam wadliwe wdrożenie wysyłki z GCC, które wymagało CopyConstructible. (Nie dotyczy to jednak problemu z OP). –

+0

@ Luc: Masz rację, źle pamiętam. –

+0

Kod działa, ale mam pytanie w linii 'std :: unique_lock l (td.m);' nie jest mutex w wątku już zablokowanym podczas sprawdzania zmiennej warunku? Chyba coś tam mi brakuje. Myślałem, że blokuje muteks w wątku, a następnie czeka, aż flaga stopu lub praca zostanie dodana do kolejki. Czy w takim przypadku główny wątek nie czekałby na blokadę wydanego? Czy to z powodu fałszywego przebudzenia, że ​​to działa? – bjackfly

3

Od momentu rozpoczęcia wątku bez argumentów, oczekuje się, że zadanie zostanie uruchomione bez żadnych argumentów, tak jakby użyto task1(). Dlatego podpis, który chcesz wspierać, nie jest int(int, int), ale int(). Z kolei oznacza to, że musisz przekazać funktor zgodny z tą sygnaturą do konstruktora z std::packaged_task<int()>. Spróbuj:

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

Inną możliwością jest:

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

ponieważ konstruktor std::thread może przyjąć argumentów. Tutaj funktor, który do niego przejdziesz, będzie użyty tak, jakby używał go task1(3, 11).

+0

Niestety, nie. Może próbuję funktora dla 'ackermann'a – towi

+0

@towi 'Nie' do czego? Ponownie wystąpił błąd kompilacji? Jaki jest błąd? –

+0

Tak, różne błędy. Myślę, że najbliższy mam (najkrótsza lista błędów) był: 'packaged_task task1 (bind (ack, 3,11)); thread th1 {move (task1)}; 'with error:' tuple: 274: 17: error: 'constexpr std :: _ Tuple_impl <### = std :: _ Tuple_impl <0ul, std :: packaged_task >]' zadeklarowane jako weź referencję const, ale niejawna deklaracja nie będzie miała stałej wartości " – towi