Nel capitolo "tutorial" 5.3.5.3 del suo libro Il linguaggio di programmazione C++ (4a edizione), Bjarne Stroustrup scrive sulla funzione std::async
.Quale limite di std :: async si riferisce a Stroustrup?
C'è un limite evidente: Non pensate nemmeno di usare
async()
per le attività che il blocco che necessitano di condividere risorse - conasync()
non si sa nemmeno quantithread
s verrà utilizzato perché è fino aasync()
decidere in base a ciò che sa sulle risorse di sistema disponibili al momento della chiamata.
Una simile esortazione può essere trovata in the C++11-FAQ on his website.
"Semplice" è l'aspetto più importante della progettazione
async()
/future
; i futures possono anche essere utilizzati con thread in generale, ma nemmeno utilizzaasync()
per avviare attività che eseguono operazioni di I/O, manipola mutex o in altri modi interagiscono con altre attività.
È interessante notare che non approfondisce questa limitazione quando ritorna alle caratteristiche di concorrenza di C++ 11 in maggior dettaglio nel § 42.4.6 del suo libro. Ancora più interessante, in questo capitolo ha effettivamente continua (confronta con la dichiarazione sul suo sito web):
un utilizzo semplice e realistico di
async()
sarebbe quello di generare un compito di raccogliere l'input da un utente.
The documentation of async
on cppreference.com
non menziona affatto tali limitazioni.
Dopo aver letto alcune proposte e discussioni che portano allo standard C++ 11 nella sua forma finale (che sfortunatamente non ho accesso a), capisco che async
è stato incorporato molto tardi nello standard C++ 11 e c'è stata molta discussione su quanto questa funzione dovrebbe essere potente. In particolare, ho trovato l'articolo Async Tasks in C++11: Not Quite There Yet by Bartosz Milewski un ottimo riassunto dei problemi che devono essere considerati quando si implementa async
.
Tuttavia, tutti i problemi discussi sono riportate thread_local
variabili che non vengono distrutti se un thread viene riciclato, bloccaporte spuri o violazioni di accesso ai dati se un thread cambia il suo compito metà azione e entrambi i compiti tenere una mutex
o recursive_mutex
rispettivamente, e così via. Queste sono serie preoccupazioni per gli implementatori della funzionalità, ma se ho capito bene, la specifica corrente di async
richiede che tutti questi dettagli siano nascosti all'utente eseguendo l'attività sul thread del chiamante o come se un nuovo thread sia stato creato per il compito.
Quindi la mia domanda è: Cosa non mi è concesso a che fare con async
che mi è permesso di fare utilizzando thread
s manualmente e qual è il motivo di questa limitazione?
Ad esempio, c'è qualcosa di sbagliato nel seguente programma?
#include <future>
#include <iostream>
#include <mutex>
#include <vector>
static int tally {};
static std::mutex tally_mutex {};
static void
do_work(const int amount)
{
for (int i = 0; i < amount; ++i)
{
// Might do something actually useful...
const std::unique_lock<std::mutex> lock {tally_mutex};
tally += 1;
}
}
int
main()
{
constexpr int concurrency {10};
constexpr int amount {1000000};
std::vector<std::future<void>> futures {};
for (int t = 0; t < concurrency; ++t)
futures.push_back(std::async(do_work, amount/concurrency));
for (auto& future : futures)
future.get();
std::cout << tally << std::endl;
}
Ovviamente, se il runtime decide di pianificare tutte le attività sul thread principale, ci sarà inutilmente acquisire il mutex più e più volte senza una buona ragione. Ma mentre questo potrebbe essere inefficiente, non è errato.
Come nell'esempio di Stroustrup: non eseguire blocchi, è possibile ottenere facilmente deadlock poiché non è garantito che async venga eseguito in un altro thread. Nel tuo esempio, blocca il mutex nella funzione principale prima di chiamare asincrona. –
@DmitriSosnik Puoi spiegare come esattamente questo potrebbe bloccarsi? Acquisire il mutex una volta nel thread principale prima di avviare le attività non sarà certo un deadlock, ma ridurrà anche l'uso di un mutex ad absurdum e darà a 'tally' un valore spazzatura. Ad ogni modo, la mia domanda è quali sono le limitazioni di 'std :: async', non è il modo migliore per impostare una variabile su 1000000. – 5gon12eder