Domanda

Vengo in gran parte da un C ++ sfondo, ma penso che questa domanda vale per filettare in qualsiasi lingua. Ecco lo scenario:

  1. Abbiamo due fili (ThreadA e ThreadB), e un valore x nella memoria condivisa

  2. Si supponga che l'accesso a x è opportunamente comandato da un mutex (o altro controllo di sincronizzazione adatto)

  3. Se i fili capita di girare su processori diversi, cosa succede se ThreadA esegue un'operazione di scrittura, ma il suo processore colloca il risultato nella cache L2 piuttosto che la memoria principale? Poi, se ThreadB cerca di leggere il valore, sarà non basta guardare nella propria L1 / L2 cache / memoria principale e poi lavorare con qualsiasi valore precedente era lì?

Se questo non è il caso, allora come è gestito questo problema?

Se questo è il caso, allora che cosa si può fare su di esso?

È stato utile?

Soluzione

Il vostro esempio avrebbe funzionato bene.

Più processori utilizzano un coerenza come MESI per garantire che i dati rimane in sync tra le cache. Con MESI, ogni linea di cache è considerato essere sia modificato, esclusivamente tenuto, condivisa tra CPU, o non valido. Scrivi linea di cache che viene condiviso tra le forze di processori di diventare invalido in un altro CPU, mantenendo le cache in sincronia.

Tuttavia, questo non è abbastanza. Diversi processori hanno e maggior parte dei processori moderni supportano un certo livello di riordinando accessi alla memoria. In questi casi, barriere di memoria sono necessari.

Per esempio, se si dispone di Discussione A:

DoWork();
workDone = true;

E Filetto B:

while (!workDone) {}
DoSomethingWithResults()

Con entrambi in esecuzione su processori separati, non v'è alcuna garanzia che le scritture fatte entro DoWork () saranno visibili per infilare B prima della scrittura per workDone e DoSomethingWithResults () sarebbe procedere con lo stato potenzialmente incoerente. barriere di memoria garantiscono un certo ordinamento della lettura e scrittura - l'aggiunta di una barriera di memoria dopo DoWork () nel thread A costringerebbe tutte le letture / scritture effettuate da DoWork per completare prima che la scrittura per workDone, in modo che Thread B potrebbe avere una visione coerente. Mutex intrinsecamente forniscono una barriera di memoria, in modo che legge / scrive non può passare una chiamata per bloccare e sbloccare.

Nel suo caso, un processore segnalerebbe agli altri che sporcate una linea cache e forzare gli altri processori per ricaricare dalla memoria. Acquisire il mutex per leggere e scrivere le garanzie di valore che il cambiamento di memoria è visibile all'altra processore nell'ordine previsto.

Altri suggerimenti

La maggior parte delle primitive di bloccaggio come mutex implicare barriere di memoria . Questi costringono un flush della cache e ricaricare a verificarsi.

Ad esempio,

ThreadA {
    x = 5;         // probably writes to cache
    unlock mutex;  // forcibly writes local CPU cache to global memory
}
ThreadB {
    lock mutex;    // discards data in local cache
    y = x;         // x must read from global memory
}

In generale, il compilatore riconosce memoria condivisa, e richiede un notevole sforzo per assicurare che la memoria condivisa è collocato in un luogo condivisibile. compilatori moderni sono molto complicate nel modo in cui essi prevedono operazioni e memoria accessi; essi tendono a comprendere la natura di threading e di memoria condivisa. Questo non vuol dire che sono perfetti, ma in generale, gran parte della preoccupazione è curato dal compilatore.

C # ha qualche configurazione in supporto a questo tipo di problemi. È possibile contrassegnare una variabile con la parola chiave volatile, che lo obbliga a essere sincronizzato su tutte le cpu.

public static volatile int loggedUsers;

L'altra parte è un wrappper sintattica circa i metodi NET chiamati Threading.Monitor.Enter (x) e Threading.Monitor.Exit (x), dove x è una variabile per bloccare. Ciò causa altri thread cercando di bloccare x dover attendere fino a che il filo di bloccaggio chiama Exit (x).

public list users;
// In some function:
System.Threading.Monitor.Enter(users);
try {
   // do something with users
}
finally {
   System.Threading.Monitor.Exit(users);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top