2009-10-20 11 views
24

Sto sviluppando un programma con multithreading in esecuzione su Linux (compilato con G ++ 4.3) e se cerchi un po 'trovi un sacco di storie spaventose su std :: string che non è thread-safe con GCC. Ciò è dovuto al fatto che internamente utilizza la copia su scrittura che provoca il caos con strumenti come Helgrind.È std :: string thead-safe con gcc 4.3?

Ho creato un piccolo programma che copia una stringa in un'altra stringa e se si controllano entrambe le stringhe condividono entrambi lo stesso puntatore _M_p interno. Quando una stringa viene modificata, il puntatore cambia in modo che la roba copy-on-write funzioni correttamente.

Ciò che mi preoccupa è che cosa succede se condivido una stringa tra due thread (ad esempio, passandola come un oggetto in un dataqueue thread-safe tra due thread). Ho già provato a compilare con l'opzione '-pthread' ma non sembra fare molta differenza. Quindi la mia domanda:

  • C'è un modo per forzare std :: string per essere protetto da thread? Non mi dispiacerebbe se il comportamento copy-on-write fosse disabilitato per raggiungere questo obiettivo.
  • Come hanno risolto altre persone? O sono paranoico?

io non riesco a trovare una risposta definitiva quindi spero che voi mi può aiutare ..

Edit:

Wow, questo è un bel po 'di risposte in un così breve tempo. Grazie! Userò sicuramente la soluzione di Jack quando voglio disabilitare COW. Ma ora la domanda principale diventa: devo davvero disabilitare COW? O la 'contabilità' fatta per thread COW è sicura? Attualmente sto navigando le fonti libstdC++, ma che sta andando a prendere un po 'di tempo per capire ...

Edit 2

OK naviga il codice sorgente libstdC++ e ho trovato qualcosa di simile in libstd ++ - v3 /include/bits/basic_string.h:

_CharT* 
    _M_refcopy() throw() 
    { 
#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING 
    if (__builtin_expect(this != &_S_empty_rep(), false)) 
#endif 
      __gnu_cxx::__atomic_add_dispatch(&this->_M_refcount, 1); 
    return _M_refdata(); 
    } // XXX MT 

Quindi c'è sicuramente qualcosa là sulle modifiche atomiche per il contatore di riferimento ...

Conclusione

Sto segnando il commento di sellibitze come risposta qui perché penso che siamo giunti alla conclusione che quest'area non è ancora stata risolta per ora. Per aggirare il comportamento della MUC suggerirei la risposta di Jack Lloyd. Grazie a tutti per una discussione interessante!

+0

+1 buona domanda! Sfortunatamente, la gente legge semplicemente "threadsafe" e pensa "No!". Meglio leggere l'intera domanda! :) – sellibitze

+0

Poiché std :: string è un'istanza del modello std :: basic_string, potrebbe essere possibile dare un'occhiata al codice sorgente. Prova a cercare qualsiasi macro che accenda/spenga la sicurezza del thread. –

+0

A proposito, il copy-on-write è lento negli ambienti multi-thread, dovresti * voler * non usarlo, non essere disposto a farlo. – GManNickG

risposta

15

Le discussioni non fanno ancora parte dello standard. Ma non penso che nessun venditore possa cavarsela senza fare std :: string thread-safe, al giorno d'oggi. Nota: esistono diverse definizioni di "thread-safe" e la mia potrebbe differire dalla tua. Ovviamente, non ha senso proteggere un container come std :: vector per l'accesso simultaneo di default anche quando non ne hai bisogno. Ciò andrebbe contro lo spirito del C++ "non pagare per cose che non usi". L'utente dovrebbe sempre essere responsabile della sincronizzazione se desidera condividere oggetti tra thread diversi. Il problema qui è se un componente di libreria utilizza e condivide alcune strutture di dati nascoste che potrebbero portare a dati razziali anche se "le funzioni sono applicate su oggetti diversi" dal punto di vista dell'utente.

La bozza C++ 0x (N2960) contiene la sezione "evitamento della corsa dati" che dice fondamentalmente che i componenti della libreria possono accedere ai dati condivisi che sono nascosti all'utente se e solo se evita attivamente possibili razze di dati. Sembra un'implementazione copy-on-write di std :: basic_string che deve essere sicura come w.r.t. multi-threading come un'altra implementazione in cui i dati interni non vengono mai condivisi tra diverse istanze di stringa.

Non sono sicuro al 100% se libstdC++ si prenda già cura di esso. Penso che lo faccia. Per essere sicuro, controlla

+1

Grazie mille per la risposta dettagliata. Controllo la pagina che hai collegato ma rimane un po 'vago a mio avviso, parlando di contenitori in generale (che dovresti fornire un blocco adeguato) e non tanto di archi. :) L'elusione della corsa dei dati sembra almeno una "scusa" per andare avanti e assumere che tutto andrà bene tra thread dal momento che mette esattamente la responsabilità sulle persone che implementano la libreria (a patto ovviamente che io come programmatore le stringhe in base al valore anziché per riferimento) ... – Benjamin

0

Nessun contenitore STL è thread-safe. In questo modo, la libreria ha uno scopo generale (entrambi da utilizzare in modalità single threading o multi threading). Nel multithreading, dovrai aggiungere il meccanismo di sincronizzazione.

+3

La differenza con la stringa però è che anche il passaggio per valore induce problemi di threading - questo può essere un po 'inaspettato, per non dire altro. –

+1

Grazie ragazzi per risposte così rapide!So che nessun contenitore STL è thread-safe (e usa correttamente i wrapper di chiusura quando ho bisogno che siano thread-safe) ma per quanto ne so lo std :: string è l'unico a "segretamente" usare lo stesso datastore per stringhe identiche. La mia preoccupazione è che la contabilità di copy-on-write non sia del tutto infondata, ho ragione io? – Benjamin

+9

Non capisco perché ci siano così tanti voti positivi. Ragazzi, si prega di prestare attenzione alla domanda reale – sellibitze

11

Se non ti interessa disattivare la copia su scrittura, questa potrebbe essere la soluzione migliore. std :: string COW funziona solo se sa che sta copiando un altro std :: string, quindi puoi farlo allocare sempre un nuovo blocco di memoria e fare una copia effettiva. Per esempio questo codice:

#include <string> 
#include <cstdio> 

int main() 
    { 
    std::string orig = "I'm the original!"; 
    std::string copy_cow = orig; 
    std::string copy_mem = orig.c_str(); 
    std::printf("%p %p %p\n", orig.data(), 
          copy_cow.data(), 
          copy_mem.data()); 
    } 

mostrerà che la seconda copia (usando c_str) impedisce COW.(Poiché std :: string vede solo un const char nudo * e non ha idea da dove provenga o quale sia la sua durata, quindi deve fare una nuova copia privata).

+12

Attenzione: l'assegnazione al risultato di c_str() troncerà la stringa se orig contiene valori null incorporati. Sarebbe più sicuro usare il metodo assign (o il costruttore) prendendo un const char * e un size_type e per passare "orig.data()" e "orig.size()" a quello. –

+0

Se decido di utilizzare il percorso 'disable COW', userò sicuramente questo (e l'osservazione aggiunta da Eric). Ben fatto :) – Benjamin

+0

@Eric Punto eccellente, non posso credere che non ho considerato come ciò avrebbe interagito con null. Grazie. –

0

Sembra che questo è stato risolto qualche tempo fa: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=5444 è stato (chiuso come lo stesso problema di http://gcc.gnu.org/bugzilla/show_bug.cgi?id=5432, che è stato risolto in 3.1).

Vedi anche http://gcc.gnu.org/bugzilla/show_bug.cgi?id=6227

+0

Penso che il bug si riferisca al codice iostream ma, non al codice stringa? – Benjamin

+0

Oh, giusto. Ho fatto riferimento a questo come quello relativo a basic_string (# 5444) è stato chiuso con la stessa risoluzione di 5432. Ho modificato la mia risposta per chiarire questo. –

+0

Grazie Eric! Finché aggiungiamo elementi di bugtracker, anche questo sembra correlato: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40518 – Benjamin

3

This sezione del libstdC++ interni afferma:

Il C++ funzionalità stringa biblioteca richiede un paio di operazioni atomiche fornire thread-sicurezza. Se non si esegue alcuna azione speciale, la libreria utilizza le versioni stub di queste funzioni non thread-safe. Funzioneranno correttamente, a meno che le vostre applicazioni siano multi-thread.

Il conteggio dei riferimenti dovrebbe funzionare in un ambiente a più thread. (a meno che il tuo sistema non fornisca gli atomici necessari)

+0

Sarebbe bello se ci fosse un modo rapido per capire se sono state utilizzate le funzioni reali o di stub. Forse guarda il codice macchina e controlla il prefisso LOCK ... –

0

Secondo l'this bug issue, l'implementazione copy-on-write di std::basic_string non è ancora completamente sicura per i thread. <ext/vstring.h> è un'implementazione senza COW e sembra fare molto meglio in un contesto di sola lettura.

Problemi correlati