È intelligente sostituire boost :: thread e boost :: mutex con equivalenti c ++ 11?


153

Motivazione: motivo per cui sto considerando che il mio geniale project manager pensa che la spinta sia un'altra dipendenza e che sia orribile perché "dipendi da essa" (ho provato a spiegare la qualità della spinta, poi ho rinunciato dopo qualche tempo :( La ragione più piccola per cui mi piacerebbe farlo è che vorrei imparare le funzionalità di c ++ 11, perché le persone inizieranno a scrivere codice in esso.

  1. Esiste un mapping 1: 1 tra #include<thread> #include<mutex>e aumenta gli equivalenti?
  2. Considereresti una buona idea sostituire gli elementi boost con elementi c ++ 11
    . Il mio utilizzo è primitivo, ma ci sono esempi in cui std non offre quale boost fa? O (blasfemia) viceversa?

PS Uso GCC, quindi ci sono le intestazioni.


46
Le linee guida per la codifica IMO di Google sono stupide in molti modi ... Per es. non consentono l'auto da C ++ 11 ... :)
NoSenseEtAl

5
Linee guida per la citazione: [auto] ostacola la leggibilità [perché rimuove] la ridondanza controllata (come i nomi dei tipi) che può essere utile per i lettori.
Andrew Tomazos,

31
per (auto it = v.begin () ... :)
NoSenseEtAl

15
@ AndrewTomazos-Fathomling: Davvero? Personalmente non credo di essermi mai preoccupato del tipo reale di iteratore (beh forse un paio di volte), solo delle operazioni supportate ... Direi che la ridondanza sintattica è raramente una buona idea (DRY).
Grizzly,

16
a proposito google ha modificato le sue stupide linee guida quindi ora finalmente consentono l'auto
NoSenseEtAl

Risposte:


192

Esistono diverse differenze tra Boost.Thread e la libreria di thread standard C ++ 11:

  • Boost supporta la cancellazione dei thread, i thread C ++ 11 no
  • C ++ 11 supporta std::async, ma Boost no
  • Boost ha un boost::shared_mutexblocco per lettore multiplo / scrittore singolo. L'analogo std::shared_timed_mutexè disponibile solo dal C ++ 14 ( N3891 ), mentre std::shared_mutexè disponibile solo dal C ++ 17 ( N4508 ).
  • I timeout C ++ 11 sono diversi dai timeout Boost (anche se questo dovrebbe presto cambiare ora Boost.Chrono è stato accettato).
  • Alcuni nomi sono diversi (ad es. boost::unique_futureVs std::future)
  • La semantica di passaggio degli argomenti di std::threadè diversa da boost::thread--- Boost utilizza boost::bind, il che richiede argomenti copiabili. std::threadconsente a tipi di solo spostamento come quelli std::unique_ptrdi essere passati come argomenti. A causa dell'uso di boost::bind, anche la semantica dei segnaposto, come _1nelle espressioni di bind nidificate, può essere diversa.
  • Se non si chiama in modo esplicito join()o detach()il boost::threaddistruttore e l'operatore di assegnazione chiameranno detach()l'oggetto thread a cui viene distrutto / assegnato. Con un std::threadoggetto C ++ 11 , ciò comporterà una chiamata std::terminate()e l'interruzione dell'applicazione.

Per chiarire il punto sui parametri di solo spostamento, il seguente è C ++ 11 valido e trasferisce la proprietà del intdal temporaneo std::unique_ptral parametro di f1quando viene avviato il nuovo thread. Tuttavia, se lo si utilizza boost::thread, non funzionerà, poiché utilizza boost::bindinternamente e std::unique_ptrnon può essere copiato. C'è anche un bug nella libreria di thread C ++ 11 fornita con GCC che impedisce questo funzionamento, poiché lo utilizza anche std::bindnell'implementazione.

void f1(std::unique_ptr<int>);
std::thread t1(f1,std::unique_ptr<int>(new int(42)));

Se stai usando Boost, puoi probabilmente passare ai thread C ++ 11 in modo relativamente indolore se il tuo compilatore lo supporta (ad es. Versioni recenti di GCC su Linux hanno un'implementazione quasi completa della libreria di thread C ++ 11 disponibile in -std=c++0xmodalità).

Se il tuo compilatore non supporta i thread C ++ 11, potresti essere in grado di ottenere un'implementazione di terze parti come Just :: Thread , ma questa è ancora una dipendenza.


1
Esistono metodi di blocco / sblocco separati per lettori e scrittori ( lock/ unlockper scrittori rispetto a "lock_shared / unlock_shared" per i lettori). Più lettori possono chiamare lock_shared senza bloccare, a condizione che nessuno lo stia utilizzando.
Dave S,

2
I shared_mutexdocumenti sono disponibili su boost.org/doc/libs/1_47_0/doc/html/thread/… . È possibile bloccare il mutex come condiviso o esclusivo, quindi utilizzare la funzione di sblocco corrispondente. Puoi anche usare i tipi RAII per fare questo ( shared_lockrichiede un blocco di lettura condiviso lock_guarde unique_lockun blocco esclusivo). Ho cercato di chiarire il punto sui tipi di sola mossa.
Anthony Williams,

3
Un'altra cosa minore che mi ha fatto inciampare: in boost, il distruttore di un thread in esecuzione lo stacca ( boost.org/doc/libs/1_47_0/doc/html/thread/… ), mentre in C ++, il distruttore di un thread in esecuzione chiama termina () (FDIS 30.3.1.3)
Cubbi,

3
In C ++ 11, la try_scoped_lockfunzionalità è coperta da std::unique_lock. Esiste un costruttore che accetta un mutex e std::try_to_lock, quindi, chiamerà try_lock()il mutex anziché lock(). Vedi stdthread.co.uk/doc/headers/mutex/unique_lock/…
Anthony Williams,

4
Sì, Boost.Thread è diventato molto più vicino allo standard C ++ 11 da quando l'ho scritto, principalmente a causa del lavoro di Vicente Botet.
Anthony Williams,

24

std::threadè in gran parte modellato dopo boost::thread, con alcune differenze :

  • la semantica di boost di one-handle-maps-to-one-os-thread di boost non viene copiata. Ma questo thread è mobile per consentire il ritorno del thread dalle funzioni di fabbrica e il posizionamento in contenitori.
  • Questa proposta aggiunge la cancellazione al boost::thread, che è una complicazione significativa. Questa modifica ha un grande impatto non solo sul thread ma anche sul resto della libreria di threading C ++. Si ritiene che questo grande cambiamento sia giustificabile a causa del vantaggio.
    • Il distruttore di thread ora deve chiamare Annulla prima del distacco per evitare perdite accidentali di thread figlio quando i thread padre vengono annullati.
    • È ora richiesto un membro di distacco esplicito per abilitare il distacco senza annullamento.
  • I concetti di handle del thread e identità del thread sono stati separati in due classi (sono la stessa classe in boost::thread). Questo per supportare una più semplice manipolazione e memorizzazione dell'identità del thread.
  • È stata aggiunta la possibilità di creare un ID thread garantito per un confronto uguale a nessun altro thread unibile ( boost::threadnon presente). Questo è utile per il codice che vuole sapere se viene eseguito dallo stesso thread di una chiamata precedente (i mutex ricorsivi sono un esempio concreto).
  • Esiste una "backdoor" per ottenere l'handle del thread nativo in modo che i client possano manipolare i thread utilizzando il sistema operativo sottostante se lo si desidera.

È del 2007, quindi alcuni punti non sono più validi: boost::threadha una native_handlefunzione ora e, come sottolineano i commentatori, std::threadnon ha più la cancellazione.

Non sono riuscito a trovare differenze significative tra boost::mutexe std::mutex.


2
std::threadnon ha cancellazione; è boost::threadquello che fa!
Anthony Williams,

@Anthony sei sicuro di non voler dire interrupt()boost :: thread? Inoltre sembra che sia una proposta originale, che è cambiata dal 2007.
Alex B

4
Sì, la cancellazione in boost si chiama "interruzione". Sì, questa è una vecchia proposta. L'ultima bozza pubblica dello standard C ++ 11 (che include la libreria thread) è open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
Anthony Williams,

6

C'è un motivo per cui non migrare std::thread.

Se si utilizza il collegamento statico, std::threaddiventa inutilizzabile a causa di questi bug / funzionalità di gcc:

Vale a dire, se si chiama std::thread::detacho std::thread::joinporterà a un'eccezione o un arresto anomalo, mentre boost::threadin questi casi funziona bene.


Vedo che un bug è NON CONFERMATO e l'altro è NON VALIDO, con un commento che dice che il giornalista avrebbe dovuto collegarsi libpthread.a. Sei assolutamente sicuro di quello che stai dicendo?
einpoklum,

1
@einpoklum, dovresti essere in grado di farlo funzionare utilizzando Wl,--whole-archive -lpthread -Wl,--no-whole-archive, vedi questa risposta ad esempio stackoverflow.com/a/23504509/72178 . Ma non è un modo molto semplice di collegarsi libpthread.ae anche considerata una cattiva idea.
ks1322,

4
Possiamo supporre che questi bug siano stati corretti, dato che è ora 2016? I bug sono stati pubblicati nel 2012 e da gcc 4.9.2, supporta ufficialmente C ++ 11, quindi non possiamo lamentarci di C ++ 11 prima del supporto ufficiale.
Splash

6

Caso aziendale

Se stai scrivendo software per l'azienda che deve funzionare su una varietà da moderata a grande di sistemi operativi e di conseguenza costruire con una varietà di compilatori e versioni di compilatori (soprattutto quelli relativamente vecchi) su tali sistemi operativi, il mio suggerimento è di stare lontano da C ++ 11 per ora. Ciò significa che non è possibile utilizzare std::threade che consiglierei di utilizzare boost::thread.

Caso di avvio tecnico / di base

Se stai scrivendo per uno o due sistemi operativi, sai per certo che dovrai sempre costruire con un compilatore moderno che supporti principalmente C ++ 11 (ad esempio VS2015, GCC 5.3, Xcode 7), e non lo sei già dipende dalla libreria boost, quindi std::threadpotrebbe essere una buona opzione.

La mia esperienza

Sono personalmente parziale rispetto a biblioteche rinforzate, molto usate, altamente compatibili, altamente coerenti come boost contro un'alternativa molto moderna. Ciò è particolarmente vero per argomenti di programmazione complicati come il threading. Inoltre, ho sperimentato a lungo un grande successo con boost::thread(e boost in generale) in una vasta gamma di ambienti, compilatori, modelli di threading, ecc. Quando è la mia scelta, scelgo boost.


1
@UmNyobe Ha ragione però. Molte implementazioni del threading C ++ 11 sono così rotte che sono sorpreso che le persone considerino persino di usarlo.
StaceyGirl

3

Con Visual Studio 2013 std::mutexsembra che si comporti in modo diverso rispetto a quello boost::mutex, che mi ha causato alcuni problemi (vedi questa domanda ).


1

Per quanto riguarda std :: shared_mutex aggiunto in C ++ 17

Le altre risposte qui forniscono un'ottima panoramica delle differenze in generale. Tuttavia, ci sono diversi problemi con std::shared_mutexquesto boost risolve.

  1. Mici aggiornabili. Questi sono assenti da std::thread. Consentono a un lettore di essere aggiornato a uno scrittore senza consentire ad altri scrittori di entrare prima di te . Questi consentono di eseguire operazioni come pre-elaborare un calcolo di grandi dimensioni (ad esempio, reindicizzare una struttura di dati) in modalità lettura, quindi eseguire l'aggiornamento per scrivere per applicare il reindex mentre si tiene il blocco in scrittura solo per un breve periodo.

  2. Equità. Se hai un'attività di lettura costante con a std::shared_mutex, i tuoi scrittori verranno bloccati indefinitamente. Questo perché se arriva un altro lettore, avranno sempre la priorità. Con boost:shared_mutex, alla fine verrà data priorità a tutti i thread . (1) Né i lettori né gli scrittori saranno affamati.

Il tl; dr di questo è che se si dispone di un sistema con un throughput molto elevato senza tempi di inattività e contese molto elevate, std::shared_mutexnon funzionerà mai per te senza la creazione manuale di un sistema prioritario su di esso. boost::shared_mutexfunzionerà immediatamente, anche se in alcuni casi potrebbe essere necessario modificarlo. Direi che std::shared_mutexil comportamento è un bug latente in attesa di accadere nella maggior parte del codice che lo utilizza.

(1) L' attuale algoritmo che utilizza si basa sullo schedulatore del thread del sistema operativo. Nella mia esperienza, quando le letture sono sature, ci sono pause più lunghe (quando si ottiene un blocco in scrittura) su Windows che su OSX / Linux.


0

Ho provato a usare shared_ptr da std invece di boost e in realtà ho trovato un bug nell'implementazione di gcc di questa classe. La mia applicazione stava andando in crash a causa del distruttore chiamato due volte (questa classe dovrebbe essere thread-safe e non dovrebbe generare tali problemi). Dopo essersi spostato su boost :: shared_ptr tutti i problemi sono scomparsi. Le attuali implementazioni di C ++ 11 non sono ancora mature.

Boost ha anche più funzionalità. Ad esempio, l'intestazione nella versione std non fornisce il serializzatore a uno stream (ovvero cout << duration). Boost ha molte librerie che usano i propri equivalenti, ecc., Ma non cooperano con le versioni standard.

Per riassumere: se hai già un'applicazione scritta usando boost, è più sicuro mantenere il tuo codice così com'è invece di fare qualche sforzo per passare allo standard C ++ 11.


4
Il shared_ptrdistruttore non ha bisogno di essere thread-safe, è un comportamento indefinito avere un thread che accede a un oggetto mentre un altro thread lo sta distruggendo. Se pensi di aver trovato un bug nel shared_ptr del GCC si prega di segnalarlo , altrimenti sull'equilibrio della probabilità che si sta utilizzando nel modo sbagliato.
Jonathan Wakely,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.