2010-06-02 14 views
5

Considerare il seguente scenario. Abbiamo una funzione C++ con una variabile locale statica:Come affrontare la sicurezza dei thread dei dati del servizio utilizzati per il mantenimento delle variabili locali statiche in C++?

void function() 
{ 
    static int variable = obtain(); 
    //blahblablah 
} 

la funzione deve essere chiamato da più thread simultaneamente, in modo da aggiungere una sezione critica per evitare l'accesso simultaneo al locale statico:

void functionThreadSafe() 
{ 
    CriticalSectionLockClass lock(criticalSection); 
    static int variable = obtain(); 
    //blahblablah 
} 

ma sarà sufficiente? Voglio dire, c'è un po 'di magia che rende la variabile inizializzata non più di una volta. Pertanto, alcuni dati di servizio gestiti dal runtime indicano se ciascun locale statico è già stato inizializzato.

La sezione critica del codice precedente proteggerà anche i dati di servizio? È necessaria una protezione extra per questo scenario?

+0

Per quanto riguarda 'gcc', c'è stata questa risposta: http://stackoverflow.com/questions/1270927/are-function-static-variables-thread-safe-in-gcc –

risposta

5

C++ dice che la variabile statica deve essere inizializzata solo una volta, tuttavia C++ non tratta i thread (ancora).

gcc (atleast on * nix systems) esegue la magia corretta per proteggere in modo sicuro più thread inizializzando tale variabile statica. Secondo http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/12f8e2c7-d94d-4904-8c79-a0b0d1088e0b, msvc non - e in tal caso dovrai bloccare tu stesso l'inizializzazione.

guardia l'inizializzazione con una sezione critica dovrebbe proteggere tutto questo - cioè il vostro functionThreadSafe() è ok - (a meno che obtain() si chiama functionThreadSafe()

http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx è la pena di leggere a questo proposito

Personalmente, a. evitare sorprese proverei a riscrivere questo in modo da poter inizializzare variable da soli, prima di creare qualsiasi thread - ad es.

static int variable = 0; 
void init_variable() //call this once, at the start of main() 
{ 
    variable = obtain(); 
} 

void function() 
{ 
    //use variable, lock if you write to it 
} 
0

Per evitare il bloccaggio in ogni caso, si può andare con questo:

void functionThreadSafe() 
{ 
    static int *variable = 0; 
    if (variable == 0) 
    { 
     CriticalSectionLockClass lock(criticalSection); 
     // Double check for initialization in different thread 
     if (variable == 0) 
     { 
      variable = new int(obtain()); 
     } 
    } 
    //blahblablah 
} 
+0

Ora si dispone di un altro locale statico. Cosa è cambiato rispetto al codice in questione? – sharptooth

+0

Non è necessario bloccare ogni chiamata alla funzione. – jopa

+0

È interessante, ma non risponde alla sicurezza dei thread dei dati del servizio utilizzati per la manutenzione del locale statico. – sharptooth

0

Alcuni schivate laterali si può provare che potrebbe risolvere il problema di fondo:

  • si potrebbe fare int variable essere un thread-local static, se i diversi thread non hanno effettivamente bisogno di condividere il valore di questa variabile o passare dati l'un l'altro attraverso di essa.
  • per un int su x86, è possibile utilizzare una lettura/scrittura atomica come da InterlockedCompareExchange() o equivalente sulla piattaforma. Ciò consente a più thread di accedere in sicurezza alla variabile senza blocchi. Funziona solo per i tipi atomici nativi all'hardware (ad es., Parole a 32 e 64 bit). Dovrai anche capire cosa fare se due thread vogliono scrivere sulla variabile contemporaneamente (cioè, uno scoprirà che l'altro ha scritto su di esso quando esegue il confronto-e-swap op).
1

(ho pubblicare questo su un'altra questione, ma è anche una risposta a questo)

Ecco il mio prendere (se proprio non è possibile inizializzare prima di filetti sono lanciati):

ho visto (e utilizzati) qualcosa di simile per la protezione di inizializzazione statico, utilizzando boost :: volta

#include <boost/thread/once.hpp> 

boost::once_flag flag; 

// get thingy 
const Thingy & get() 
{ 
    static Thingy thingy; 

    return thingy; 
} 

// create function 
void create() 
{ 
    get(); 
} 

void use() 
{ 
    // Ensure only one thread get to create first before all other 
    boost::call_once(&create, flag); 

    // get a constructed thingy 
    const Thingy & thingy = get(); 

    // use it 
    thingy.etc..()   
} 

Nella mia comprensione, in questo modo tutte le discussioni attendere il boost :: call_once exc ept uno che creerà la variabile statica. Verrà creato solo una volta e quindi non verrà mai più chiamato. E poi non hai più nessun lucchetto.

Problemi correlati