Sono in esecuzione su una macchina a 32 bit e sono in grado di confermare che i valori lunghi possono lacerare utilizzando il seguente frammento di codice che colpisce molto velocemente.Simulare la rottura di una doppia in C#
static void TestTearingLong()
{
System.Threading.Thread A = new System.Threading.Thread(ThreadA);
A.Start();
System.Threading.Thread B = new System.Threading.Thread(ThreadB);
B.Start();
}
static ulong s_x;
static void ThreadA()
{
int i = 0;
while (true)
{
s_x = (i & 1) == 0 ? 0x0L : 0xaaaabbbbccccddddL;
i++;
}
}
static void ThreadB()
{
while (true)
{
ulong x = s_x;
Debug.Assert(x == 0x0L || x == 0xaaaabbbbccccddddL);
}
}
Ma quando provo qualcosa di simile con il doppio, non riesco a strappare. Qualcuno sa perché? Per quanto posso dire dalle specifiche, solo l'assegnazione a un float è atomica. L'assegnazione a un doppio dovrebbe avere il rischio di strappi.
static double s_x;
static void TestTearingDouble()
{
System.Threading.Thread A = new System.Threading.Thread(ThreadA);
A.Start();
System.Threading.Thread B = new System.Threading.Thread(ThreadB);
B.Start();
}
static void ThreadA()
{
long i = 0;
while (true)
{
s_x = ((i & 1) == 0) ? 0.0 : double.MaxValue;
i++;
if (i % 10000000 == 0)
{
Console.Out.WriteLine("i = " + i);
}
}
}
static void ThreadB()
{
while (true)
{
double x = s_x;
System.Diagnostics.Debug.Assert(x == 0.0 || x == double.MaxValue);
}
}
Domanda stupida - che cosa è lo strappo? – Oded
le operazioni su ints sono garantite per essere atomiche per quanto riguarda l'accesso da più thread. Non così con i lunghi. Tearing sta ottenendo un mix di due valori intermedi (cattivo). Si sta chiedendo perché lo stesso non si vede in doppio, poiché anche i doppi non garantiscono le operazioni atomiche. – hatchet
@Oded: su macchine a 32 bit, vengono scritti solo 32 bit alla volta. Se si sta scrivendo un valore a 64 bit su una macchina a 32 bit e si scrive allo stesso indirizzo contemporaneamente su due thread diversi, si hanno effettivamente * quattro * scritture, non * due *, perché le scritture vengono eseguite a 32 bit in un tempo. È quindi possibile che i thread si muovano e quando il fumo cancella la variabile contiene i primi 32 bit scritti da un thread e i 32 bit inferiori scritti dall'altro. Quindi puoi scrivere 0xDEADBEEF00000000 su un thread e 0x00000000BAADF00D su un altro e finire con 0x0000000000000000 in memoria. –