2014-10-09 14 views
9

Sto imparando a conoscere i mutex in C++ e ho un problema con il seguente codice (tratto dalla "Libreria standard C++ di N. Josuttis").Il programma C++ blocca/getta inaspettatamente

Non capisco il motivo per cui si blocca/getta a meno che io aggiungothis_thread::sleep_for nel thread principale (allora non blocca e tutte e tre le chiamate sono svolte).

Il compilatore è cl.exe utilizzato dalla riga di comando.

#include <future> 
#include <mutex> 
#include <iostream> 
#include <string> 
#include <thread> 
#include <chrono> 

std::mutex printMutex; 

void print(const std::string& s) 
{ 
    std::lock_guard<std::mutex> lg(printMutex); 

    for (char c : s) 
    { 
     std::cout.put(c); 
    } 
    std::cout << std::endl; 
} 

int main() 
{ 
    auto f1 = std::async(std::launch::async, print, "Hello from thread 1"); 
    auto f2 = std::async(std::launch::async, print, "Hello from thread 2"); 

    // std::this_thread::sleep_for(std::chrono::seconds(1)); 

    print(std::string("Hello from main"));  
} 
+3

Non stai aspettando che i fili finiscano. Penso che questo sia un problema con la libreria MSVC qui. – Niall

risposta

11

Penso che quello che state vedendo è un problema con la conformità dell'implementazione MSVC di async (in combinazione con future). Credo che sia not conformant. Sono in grado di riprodurlo con VS2013, ma non riesco a riprodurre il problema con gcc.

L'arresto anomalo si verifica perché il thread principale termina (e inizia a ripulire) prima che gli altri due thread siano completi.

Quindi un semplice ritardo (sleep_for) o .get() o .wait() sui due futuri dovrebbe risolvere il problema per voi. Così potrebbe apparire il main modificato;

int main() 
{ 
    auto f1 = std::async(std::launch::async, print, "Hello from thread 1"); 
    auto f2 = std::async(std::launch::async, print, "Hello from thread 2"); 

    print(std::string("Hello from main"));  

    f1.get(); 
    f2.get(); 
} 

Favorire l'attesa esplicita o superare il "sonno" a tempo.

Note sulla conformità

C'era una proposta Herb Sutter di cambiare l'attesa o il blocco dello stato condiviso della future tornato da async. Questo potrebbe essere il motivo del comportamento in MSVC, potrebbe essere visto come aver implementato la proposta. Non sono sicuro di quale sia stato il risultato finale della proposta o della sua integrazione (o parte di essa) in C++ 14. Almeno il blocco di da async restituisce . Sembra che il comportamento di MSVC non sia stato incluso nelle specifiche.

È interessante notare che la dicitura in §30.6.8/5 modificato;

Da C++ 11

una chiamata a una funzione di attesa su un oggetto ritorno asincrona che condivide lo stato condiviso creato presente async chiamata deve bloccare finché il thread associato ha completato, come congiunti

per C++ 14

una chiamata a una funzione di attesa su un oggetto ritorno asincrona che condivide t ha condiviso stato creato da questo async chiamata deve bloccare fino a quando il thread associato ha completato, come se uniti, o il tempo altrimenti out

io non sono sicuro di come il "time out" verrebbe specificato, Immagino sia definita l'implementazione.

+0

Quindi, qual è il comportamento previsto? I thread in esecuzione senza alcun riferimento a loro, forse dopo che il thread principale è terminato, dovrebbero continuare a funzionare, in modo simile al tacchino con la testa mozzata – Mikhail

+0

@Mikhail. Mi piace l'analogia con la Turchia, lol. Penso che questo sia il motivo per cui la proposta è stata probabilmente respinta (almeno la parte in cui i fili sono dissociati da qualsiasi stato di attesa). Penso che sia un'idea migliore per il 'futuro' restituito da 'async' per bloccare. Penso anche che a volte ci sia confusione intorno all'origine di un 'futuro' e che si blocchi o meno - forse un 'async_future' potrebbe essere stato un'idea migliore, ma non lo so. Credo che 'async' fosse un'aggiunta tardiva, quindi potrebbero non aver risolto tutti i problemi più sottili. – Niall

1

std::async restituisce un future. Le sue destructor blocchi se get o wait non è stato chiamato:

può bloccare se tutte le seguenti condizioni: lo stato condiviso è stato creato da una chiamata a std :: asincrona, lo stato condiviso non è ancora pronto e questo era l'ultimo riferimento allo stato condiviso.

Vedere std::futures from std::async aren't special! per un trattamento dettagliato del soggetto.

+2

Quindi, come spiega l'incidente? –

+2

Come spiega che funziona se dorme per un secondo? –

1

aggiungere queste 2 righe alla fine del main:

f1.wait(); 
f2.wait(); 

Questo farà in modo i fili terminano prima di main esiste.