2012-03-23 9 views
5

Ho alcune domande sull'utilizzo del blocco per proteggere la mia struttura di dati condivisa. Sto usando C/C++/objC/objC++Multithreading: devo proteggere la mia variabile nel metodo di sola lettura?

Per esempio ho una classe contatore che utilizzato in ambiente multi-thread

class MyCounter { 
private: 
    int counter; 
    std::mutex m; 

public: 
    int getCount() const { 
     return counter; 
    } 
    void increase() { 
     std::lock_guard<std::mutex> lk(m); 
     counter++; 
    } 
}; 
  1. ho bisogno di usare std::lock_guard<std::mutex> lk(m); in getCount() metodo per farlo thread-safe?

  2. Cosa succede se ci sono solo due thread: un thread di lettura e un thread di scrittura quindi devo proteggerlo del tutto? Perché c'è solo un thread sta modificando la variabile, quindi penso che nessun aggiornamento perso accadrà.

  3. Se ci sono più scrittore/lettore per una variabile di tipo primitivo condiviso (ad esempio int), cosa può accadere se si blocca solo nel metodo di scrittura ma non nel metodo di lettura? Il tipo a 8 bit farà la differenza rispetto al tipo a 64 bit?

  4. I tipi primitivi sono atomici per impostazione predefinita? Ad esempio, scrivere su un char è sempre atomico? (So ​​che questo è vero in Java, ma non so di C++ e sto usando il compilatore LLVM su Mac se le cose piattaforma)

+1

"C/C++/objc/objC++" Queste sono tutte le lingue completamente diverse. Sceglierne uno; sembri aver scelto C++. – GManNickG

+0

Sto usando ObjC per GUI e C++ per il backend e ObjC++ per combinarli. Anche ObjC++ è superset degli altri. Ho appena scelto C++ come esempio. –

+0

Va bene, ma non combinarli in un elenco come uno. :) Dì solo che sei interessato a una risposta in quattro lingue diverse. – GManNickG

risposta

7

Sì, a meno che non si può garantire che le modifiche alla variabile sottostante counter sono atomiche, hai bisogno del mutex.

esempio

classica, dire counter è un valore a due byte che è incrementato nelle fasi (non atomici):

add 1 to lower byte 
if lower byte is 0: 
    add 1 to upper byte 

e il valore iniziale è 255.

Se un altro filo viene in qualsiasi punto tra la modifica del byte inferiore e la modifica del byte superiore, leggerà 0 anziché 255 (pre-incremento) o 256 (post-incremento) corretto.

In termini di quali tipi di dati sono atomici, l'ultimo standard C++ li definisce nell'intestazione <atomic>.

Se il numero di telefono non ha ha capacità C++ 11, quindi è verso l'implementazione quali tipi sono atomici.

+0

Stavo pensando che stiamo usando una CPU a 32/64 bit che dovrebbe aggiornare una variabile di dimensione a 32 bit alla volta senza dividerla in due passaggi? –

+0

@ xlc0212, la parola chiave è "dovrebbe". Con tutti i mezzi, se puoi verificarlo, potresti stare bene. Ma non è solo una questione di thread su una CPU, potresti usare più CPU, interrupt e tutti i tipi di altre cose meravigliose che accadono sotto le copertine. – paxdiablo

+0

@ xlc0212 che non funzionerà (in modo affidabile/prevedibile); quello che hai è consistenza sequenziale, non consistenza rigida. quello che osservi se il trattamento sequenziale come rigoroso sarà "vicino, ma non del tutto", e il programma avrà solo cattivi bachi. – justin

3

Sì, in questo caso è necessario bloccare anche la lettura.

Ci sono diverse alternative: qui un blocco è piuttosto pesante. Le operazioni atomiche sono le più ovvie (senza blocco). Esistono anche altri approcci al blocco in questo progetto: il blocco di scrittura in lettura è un esempio.

+1

+1, ma come nota: i tipi 'std :: atomic' in C++ non sono garantiti come liberi da lock. Molto probabilmente saranno lock free per interi sulla maggior parte delle piattaforme, ma non è una garanzia esplicita (puoi comunque controllarlo). – KillianDS

+0

@ KillianDS grazie - buon punto. – justin

1

Sì, credo che sia necessario bloccare anche la lettura. Ma dal momento che stai usando le caratteristiche del C++ 11, perché non usi invece lo std::atomic<int> counter;?

+0

questo è un altro problema: '#error non è implementato'. Ciò è accaduto se provo a '#include ' –

0

Non è possibile garantire che più thread non modificano la variabile allo stesso tempo. e se si verifica una situazione di questo tipo la tua variabile sarà confusa o il programma potrebbe bloccarsi. Per evitare tali casi è sempre meglio e più sicuro rendere sicuro il thread del programma.

È possibile utilizzare i techinques sincronizzazione disponibili come: Mutex, Lock, attributo di sincronizzazione (disponibile per MS C++)

+0

Ha effettivamente garantito che più thread non possono scrivere allo stesso tempo. Non è ancora corretto omettere un blocco di lettura ma per altri motivi (pubblicato in altre risposte). – KillianDS

+0

Come può essere così sicuro in una situazione a thread multipli senza utilizzare la sincronizzazione? –

+0

'increase' (l'unico metodo non const) utilizza la sincronizzazione e i dati condivisi sono' private' – MSalters

Problemi correlati