Domanda

Sulle piattaforme più comuni (la più importante è x86; capisco che alcune piattaforme hanno modelli di memoria estremamente difficili che non forniscono quasi nessuna garanzia utile per il multithreading, ma non mi interessano i rari esempi contrari), è il seguente codice sicuro?

Discussione 1:

someVariable = doStuff();
atomicSet(stuffDoneFlag, 1);

Discussione 2:

while(!atomicRead(stuffDoneFlag)) {}  // Wait for stuffDoneFlag to be set.
doMoreStuff(someVariable);

Supponendo implementazioni standard e ragionevoli di operazioni atomiche:

  1. L'assegnazione del thread 1 a someVariable è garantita per essere completata prima che venga chiamato atomicSet () ?
  2. Il thread 2 è garantito per vedere l'assegnazione a someVariable prima di chiamare doMoreStuff () purché legga atomicamente stuffDoneFlag ?

Modifiche:

  1. L'implementazione delle operazioni atomiche che sto usando contiene l'istruzione x86 LOCK in ciascuna operazione, se ciò aiuta.
  2. Supponiamo che stuffDoneFlag sia in qualche modo cancellato correttamente. Come non è importante.
  3. Questo è un esempio molto semplificato. L'ho creato in questo modo in modo da non dover comprendere l'intero contesto del problema per rispondere. So che non è efficiente.
È stato utile?

Soluzione

Se il tuo vero codice x86 ha l'archivio in someVariable prima dell'archivio in atomicSet nel thread 1 e il caricamento di someVariable dopo il caricamento in atomic Leggi nel thread 2, allora dovresti andare bene. Manuale per gli sviluppatori di software Intel Volume 3A specifica il modello di memoria per x86 nella Sezione 8.2 e qui dovrebbero essere sufficienti i limiti di archivio-negozio e carico-carico intra-thread.

Tuttavia, potrebbe non esserci nulla che impedisce al compilatore di riordinare le istruzioni generate da qualsiasi linguaggio di livello superiore che si sta utilizzando nelle operazioni atomiche.

Altri suggerimenti

1) Sì

2) Sì

Entrambi funzionano.

Questo codice sembra sicuro, ma metto in dubbio l'efficienza del tuo spinlock (il mentre loop) a meno che non giri solo per un periodo di tempo molto breve. Non vi è alcuna garanzia su un determinato sistema che Thread 2 non tratterà completamente tutto il tempo di elaborazione.

Consiglierei di usare alcune primitive di sincronizzazione effettive (sembra boost :: condition_variable è quello che vuoi qui) invece di fare affidamento sul blocco spin.

Le istruzioni atomiche assicurano che il thread 2 attenda che il thread 1 completi l'impostazione della variabile prima che il thread 2 proceda. Vi sono, tuttavia, due problemi chiave:

1) il someVariable deve essere dichiarato ' volatile 'per garantire che il compilatore non ottimizzi la sua allocazione, ad es. memorizzandolo in un registro o rinviando una scrittura.

2) il secondo thread sta bloccando mentre attende il segnale (definito spinlocking ). La tua piattaforma probabilmente fornisce primitive e meccanismi di blocco e segnalazione molto migliori, ma un miglioramento relativamente semplice sarebbe semplicemente sleep () nel del thread 2 mentre () body.

dsimcha scritto: " Supponiamo che stuffDoneFlag sia cancellato correttamente in qualche modo. Quanto non è importante. & Quot; Questo non è vero!

Vediamo lo scenario:

  1. Thread2 controlla stuffDoneFlag se è 1 inizia a leggere someVariable.
  2. Prima che Thread2 finisca di leggere l'utilità di pianificazione, la interrompe e la sospende per qualche tempo.
  3. Thread1 accede nuovamente a someVariable e modifica il contenuto della memoria.
  4. L'utilità di pianificazione riaccende Thread2 e continua il lavoro ma il contenuto della memoria di someVariable viene modificato!
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top