2013-06-24 20 views
5

Quello che voglio fare dovrebbe essere abbastanza facile, ma io non capisco ...Usa funzione membro in std :: packaged_task

Tutto quello che voglio fare è quello di avviare una funzione di membro di una classe in sfondo ad un certo punto nel tempo. Il risultato di tale funzione dovrebbe anche essere disponibile "esternamente". Quindi voglio preparare l'attività nel costruttore (impostando la variabile futura, ...) e avviarla in un secondo momento.

Ho provato a combinare std: :(​​packaged_task | async | future) ma non l'ho fatto funzionare.

Questo frammento non verrà compilato, ma penso che mostra quello che voglio fare:

class foo { 
private: 
    // This function shall run in background as a thread 
    // when it gets triggered to start at some certain point 
    bool do_something() { return true; } 

    std::packaged_task<bool()> task; 
    std::future<bool> result; 

public: 
    foo() : 
    task(do_something), // yes, that's wrong, but how to do it right? 
    result(task.get_future()) 
    { 
    // do some initialization stuff 
    ..... 
    } 
    ~foo() {} 

    void start() { 
    // Start Task as asynchron thread 
    std::async as(std::launch::async, task); // Also doesn't work... 
    } 

    // This function should return the result of do_something 
    bool get_result() { return result.get(); } 
}; 

Grazie in anticipo!

+0

'std :: async' è una funzione, non un tipo –

risposta

8

Basta usare std::bind():

#include <functional> // For std::bind() 

foo() : 
    task(std::bind(&foo::do_something, this)), 
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
    result(task.get_future()) 
{ 
    // ... 
} 

Inoltre, si sta facendo la cosa sbagliata qui:

std::async as(std::launch::async, task) 
//   ^^ 
//   Trying to declare a variable? 

Dal momento che ciò che si vuole è quella di chiamare la funzione std::async(), non dichiarare un oggetto di una (non esistente) tipo std::async(). Quindi, come primo passo, cambiare questo in:

std::async(std::launch::async, task) 

noti, tuttavia, che questo non sarà sufficiente per ottenere il compito in esecuzione in modo asincrono: a causa del comportamento strano della std::async() quando il futuro restituito viene scartata, il vostro compito verrà sempre eseguito come se lo avvii in modo sincrono - il distruttore dell'oggetto restituito si bloccherà fino al completamento dell'operazione. (*)

Per risolvere quest'ultimo problema, si può tenere il futuro restituita nella variabile membro result (anziché assegnare al result futuro restituito da std::packaged_task::get_future() su di costruzione):

result = std::async(std::launch::async, task); 
// ^^^^^^^^ 

(*) I pensare a che MSVC ignora questa specifica ed esegue effettivamente l'attività in modo asincrono. Quindi, se stai lavorando con VS2012, potresti non soffrire di questo problema.

EDIT:

As correctly mentioned by Praetorian in his answer, quanto sopra sarebbe ancora problematico, dal momento che una copia del packaged_task sarebbe tentata ad un certo punto della realizzazione di async(). Per ovviare a questo problema, si avvolge l'oggetto task in un wrapper di riferimento utilizzando std::ref().

+0

Grande, grazie. Che funzioni. Ma come posso iniziare il compito in modo asincrono? std :: async as (std :: launch :: async, task) continua a non funzionare – rralf

+1

@rralf: Questo perché (1) stai dichiarando un oggetto, non chiamando una funzione e (2) a causa di uno strano blocco comportamento di 'async()' quando non si utilizza il futuro restituito. Basta fare 'auto f = async (...)'. Vedere la risposta modificata :) –

+0

Penso che l'ultimo blocco di codice sia sbagliato. Sbarazzati di 'as'. – 0x499602D2

4

do_something() è una funzione membro, il che significa che accetta un puntatore implicito this come primo argomento. È necessario bind il puntatore this oppure creare un lamda che richiama do_something.

foo() : 
    task(std::bind(&foo::do_something, this)), 
    result(task.get_future()) 
{} 

o

foo() : 
    task([this]{ return do_something(); }), 
    result(task.get_future()) 
{} 

std::async as(std::launch::async, task); 

std::async è un modello di funzione, non è un tipo. Così il cambiamento evidente è

std::async(std::launch::async, task); 

Ma che causa l'ennesimo errore perché da qualche parte all'interno delle viscere di quella chiamata una copia di task è tentato di sorprendere std::packaged_task ha un costruttore di copia cancellato. È possibile risolvere il problema utilizzando std::ref, che eviterà la copia.

std::async(std::launch::async, std::ref(task)); 
Problemi correlati