2012-04-17 10 views
11

Sto usando vc2011 e si scopre che std :: async (std :: launch :: async, ...) è un po 'buggato (a volte non genera nuovi thread e li esegue in parallelo, ma riutilizza invece i thread e le attività vengono eseguiti uno dopo l'altro). Questo è troppo lento quando sto facendo chiamate di rete costose. Quindi ho pensato di scrivere la mia funzione asincrona. Mi sto bloccando però, dove dovrebbe std :: promettere live? Nella funzione 1) thread, 2) funzione asincrona o 3) funzione chiamante.Sostituzione di std :: async con la propria versione ma dove dovrebbe std :: promise live?

Codice:

#include <future> 
#include <thread> 
#include <iostream> 
#include <string> 
#include <vector> 

std::string thFun() { 
    throw std::exception("bang!"); 
    return "val"; 
} 

std::future<std::string> myasync(std::promise<std::string>& prms) { 
//std::future<std::string> myasync() { 
    //std::promise<std::string> prms; //needs to outlive thread. How? 

    std::future<std::string> fut = prms.get_future(); 
    std::thread th([&](){ 
     //std::promise<std::string> prms; //need to return a future before... 
     try { 
      std::string val = thFun(); 

      prms.set_value(val); 

     } catch(...) { 
      prms.set_exception(std::current_exception()); 
     } 

    }); 

    th.detach(); 
    return fut; 
} 

int main() { 

    std::promise<std::string> prms; //I really want the promise hidden iway in the myasync func and not live here in caller code but the promise needs to outlive myasync and live as long as the thread. How do I do this? 
    auto fut = myasync(prms); 

    //auto fut = myasync(); //Exception: future already retrieved 

    try { 
     auto res = fut.get(); 
     std::cout << "Result: " << res << std::endl; 

    } catch(const std::exception& exc) { 
     std::cout << "Exception: " << exc.what() << std::endl; 
    } 

} 

I cant sembrano andare oltre il fatto che lo std :: promessa ha bisogno di sopravvivere alla funzione asincrona (e vivere più a lungo il filo), quindi la sopraelevazione promessa dal vivo come variabile locale nella funzione asincrona. Ma la std :: promise non dovrebbe vivere anche nel codice del chiamante, poiché il chiamante deve solo conoscere i futures. E non so come rendere la promessa live nella funzione thread in quanto async deve restituire un futuro prima ancora che chiama il thread func. Mi sto grattando la testa su questo.

Qualcuno ha qualche idea?

Edit: sto evidenziando questo qui come il commento principale è un po 'disinformato. Mentre l'impostazione predefinita per std :: asycn è la modalità dererred, quando una politica di avvio di std :: launch :: async è impostata in modo esplicito, deve comportarsi come se "i thread venissero generati ed eseguiti contemporaneamente (vedere wording in it .cppreference.com/w/cpp/thread/asincrona). Vedere l'esempio in pastebin.com/5dWCjjNY per un caso in cui questo non è il comportamento visto in vs20011. La soluzione funziona alla grande e ha velocizzato la mia applicazione del mondo reale di un fattore 10.

Modifica 2: MS ha corretto il bug. Maggiori informazioni qui: https://connect.microsoft.com/VisualStudio/feedback/details/735731/std-async-std-launch-async-does-not-behave-as-std-thread

+9

"a volte non genera nuovi thread e li esegue in parallelo, ma invece riutilizza i thread ed esegue attività una dopo l'altra" Non è bacato; è così che è permesso lavorare. Non vi è alcuna garanzia nelle specifiche che qualsiasi particolare chiamata asincrona venga eseguita in un thread diverso da chiamate asincrone precedenti o future.Se lo desideri, crea solo un gruppo di thread, incollali in un contenitore e poi unisciti a loro quando vuoi recuperare i dati. –

+1

In futuro, inserire il codice direttamente nella domanda piuttosto che collegarsi a un sito esterno. – ildjarn

+0

@Nicol. Sei sicuro? Come capisco quando uso std :: launch :: async dovrebbe agire come se fosse stato generato un thread. Secondo http://en.cppreference.com/w/cpp/thread/async "Se policy & std :: launch :: async! = 0 (il bit async è impostato), genera un nuovo thread di esecuzione come se std :: thread (f, args ...), tranne che se la funzione f restituisce un valore o genera un'eccezione, viene memorizzato nello stato condiviso accessibile tramite std :: future che async restituisce al chiamante. " – petke

risposta

21

Ecco una soluzione:

future<string> myasync() 
{ 
    auto prms = make_shared<promise<string>>(); 

    future<string> fut = prms->get_future(); 

    thread th([=](){ 

     try { 
      string val = thFun(); 
      // ... 
      prms->set_value(val); 

     } catch(...) { 
      prms->set_exception(current_exception()); 
     } 

    }); 

    th.detach(); 

    return fut; 
} 

Assegnare promessa sul mucchio, e poi passare per valore [=] uno shared_ptr ad esso attraverso la lambda.

+2

Wow ! Che funzioni. Ti darei un milione di punti se potessi. Hai fatto la mia settimana. – petke

5

È necessario spostare la promessa nella nuova discussione. La risposta di Andrew Tomazos viene creata creando un std::promise con proprietà condivisa, in modo che entrambi i thread possano mantenere la promessa, e quando quello corrente ritorna dall'ambito corrente solo il nuovo thread possiede la promessa, cioè la proprietà è stata trasferita. Ma std::promise è mobile quindi dovrebbe essere possibile spostarlo direttamente nel nuovo thread, eccetto che la soluzione "ovvia" di catturarlo non funziona perché lambda non può catturare per mossa, solo per copia (o per riferimento, che non funzionerebbe come si otterrebbe un riferimento ciondolante.)

Tuttavia, std::thread supporta il passaggio di oggetti rvalue alla funzione di avvio del nuovo thread. in modo da poter dichiarare il lambda di prendere un argomento std::promise per valore, vale a dire passare il promise al lambda invece di catturarla, e quindi spostare la promessa in uno degli argomenti del std::thread esempio

std::future<std::string> myasync() { 
    std::promise<std::string> prms; 

    std::future<std::string> fut = prms.get_future(); 
    std::thread th([&](std::promise<std::string> p){ 
     try { 
      std::string val = thFun(); 

      p.set_value(val); 

     } catch(...) { 
      p.set_exception(std::current_exception()); 
     } 

    }, std::move(prms)); 

    th.detach(); 
    return fut; 
} 

questa mossa il promessa nell'oggetto std::thread, che quindi lo sposta (nel contesto del nuovo thread) nel parametro p di lambda.

Problemi correlati