Domanda

Una domanda abbastanza semplice, ma non la vedo posta da nessuna parte.

Diciamo che abbiamo una struttura globale (in C) in questo modo:

struct foo {
  int written_frequently1;
  int read_only;
  int written_frequently2;
};

Mi sembra chiaro che se abbiamo molti thread di lettura e scrittura, abbiamo bisogno di un semaforo (o altro blocco) sui membri written_frequently , anche per la lettura, dal momento che non possiamo essere Sicuro al 100% che gli incarichi di questa struttura saranno atomici.

Se vogliamo che molti thread leggano il membro read_only e nessuno che scriva, abbiamo bisogno di un semaforo sull'accesso alla struttura solo per la lettura?

(Sono propenso a dire di no, perché il fatto che le posizioni immediatamente prima e dopo vengano costantemente modificate non dovrebbe influire sul membro read_only e più thread che leggono il valore non dovrebbero interferire tra loro. Ma non ne sono sicuro.)


[Modifica: ora mi rendo conto che avrei dovuto porre questa domanda molto meglio, al fine di chiarire molto specificamente cosa intendevo dire. Naturalmente, quando ho posto la domanda per la prima volta, non ho davvero risolto tutti i problemi. Naturalmente, se modifico ora la domanda in modo completo, rovinerò tutte queste grandi risposte. Quello che volevo dire è più simile a:

struct bar {
  char written_frequently1[LONGISH_LEN];
  char read_only[LONGISH_LEN];
  char written_frequently2[LONGISH_LEN];
};

Il problema principale che mi sono posto è che, poiché questi dati fanno parte di una struttura, sono influenzati dagli altri membri della struttura e potrebbero influenzarli in cambio?

Il fatto che i membri fossero ints, e quindi le scritture sono probabilmente atomiche, in questo caso è solo un'aringa rossa.]

È stato utile?

Soluzione 10

Mille grazie a tutti i grandi risponditori (e per tutte le grandi risposte).

Per riassumere:

Se esiste un membro di sola lettura di una struttura (nel nostro caso, se il valore è impostato una volta, molto prima che un thread possa volerlo leggere), i thread che leggono questo membro non hanno bisogno di blocchi, mutex, semafori o qualsiasi altra protezione della concorrenza.

Questo è vero anche se gli altri membri vengono scritti frequentemente. Il fatto che le diverse variabili facciano tutte parte della stessa struttura non fa alcuna differenza.

Altri suggerimenti

È necessario un mutex per garantire che un'operazione sia atomica. Quindi, in questo caso particolare, potrebbe non essere necessario affatto un mutex. In particolare, se ogni thread scrive su un elemento e la scrittura è atomica e il nuovo valore è indipendente dal valore corrente di qualsiasi elemento (incluso se stesso), non ci sono problemi.

Esempio: ciascuno dei vari thread aggiorna un " last_updated_by " variabile che registra semplicemente l'ultimo thread che lo ha aggiornato. Chiaramente, fintanto che la variabile stessa viene aggiornata atomicamente, non si verificheranno errori.


Tuttavia, hai bisogno di un mutex per garantire coerenza se un thread legge o scrive più di un elemento alla volta, in particolare perché dici che stai bloccando un elemento anziché l'intera struttura .

Esempio: una discussione aggiorna il " giorno " ;, " mese " e "anno" elementi di una struttura. Ciò deve avvenire atomicamente, affinché un altro thread non legga la struttura dopo il "mese" incrementi ma prima del "giorno" termina con 1, per evitare date come il 31 febbraio. Nota che devi onorare il mutex durante la lettura; altrimenti potresti leggere un valore errato, semi aggiornato.

Se il membro read_only è effettivamente di sola lettura, non vi è alcun pericolo che i dati vengano modificati e quindi non è necessaria la sincronizzazione. Questi potrebbero essere i dati impostati prima dell'avvio dei thread.

Dovrai sincronizzare tutti i dati che possono essere scritti, indipendentemente dalla frequenza.

" Sola lettura " è un po 'fuorviante, poiché la variabile viene scritta almeno una volta quando viene inizializzata. In tal caso è ancora necessaria una barriera di memoria tra la scrittura iniziale e le letture successive se si trovano in thread diversi, altrimenti potrebbero vedere il valore non inizializzato.

Anche i lettori hanno bisogno dei mutex!

Sembra esserci un malinteso comune sul fatto che i mutex siano solo per gli scrittori e che i lettori non ne abbiano bisogno. Questo è sbagliato, e questo malinteso è responsabile di bug estremamente difficili da diagnosticare.

Ecco perché, sotto forma di un esempio.

Immagina un orologio che si aggiorna ogni secondo con il codice:

if (++seconds > 59) {        // Was the time hh:mm:59?
   seconds = 0;              // Wrap seconds..
   if (++minutes > 59)  {    // ..and increment minutes.  Was it hh:59:59?
     minutes = 0;            // Wrap minutes..
     if (++hours > 23)       // ..and increment hours.  Was it 23:59:59?
        hours = 0;           // Wrap hours.
    }
}

Se il codice non è protetto da un mutex, un altro thread può leggere le variabili ore , minuti e secondi mentre un aggiornamento è in corso. Seguendo il codice sopra:

[Start just before midnight] 23:59:59
[WRITER increments seconds]  23:59:60
[WRITER wraps seconds]       23:59:00
[WRITER increments minutes]  23:60:00
[WRITER wraps minutes]       23:00:00
[WRITER increments hours]    24:00:00
[WRITER wraps hours]         00:00:00

Il tempo non è valido dal primo incremento fino all'operazione finale sei passaggi dopo . Se un lettore controlla l'orologio durante questo periodo, vedrà un valore che potrebbe non essere solo errato ma illegale . E poiché è probabile che il tuo codice dipenda dall'orologio senza visualizzare l'ora direttamente, questa è una classica fonte di "rimbalzo". errori notoriamente difficili da rintracciare.

La correzione è semplice.

Circonda il codice di aggiornamento dell'orologio con un mutex e crea una funzione di lettore che blocca anche il mutex mentre viene eseguito. Ora il lettore attenderà il completamento dell'aggiornamento e lo scrittore non modificherà i valori a metà lettura.

No.

In generale sono necessari semafori per impedire l'accesso simultaneo alle risorse (un int in questo caso). Tuttavia, poiché il membro read_only è di sola lettura, non cambierà tra / durante gli accessi. Nota che non deve nemmeno essere una lettura atomica - se nulla cambia, sei sempre al sicuro.

Come stai impostando read_only inizialmente?

Se tutti i thread stanno solo leggendo, non hai bisogno di un semaforo.

Potresti divertirti a leggere uno di questi articoli su programmazione pratica senza blocchi , o solo sezionare e comprendere i frammenti forniti.

Vorrei nascondere ogni campo dietro una chiamata di funzione. I campi di sola scrittura avrebbero un semaforo. La sola lettura restituisce solo il valore.

Aggiunta a risposte precedenti:

  1. In questo caso il paradigma di sincronizzazione naturale è l'esclusione reciproca, non i semafori.
  2. Sono d'accordo che non è necessario alcun mutex su variabili di sola lettura.
  3. Se la parte di lettura / scrittura della struttura presenta vincoli di coerenza, in generale sarà necessario uno mutex per tutti , al fine di mantenere atomiche le operazioni.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top