Per favore perdoni il mio titolo un po 'umoristico. Io uso due definizioni diverse della parola "sicuro" in essa (ovviamente).È sicuro utilizzare le funzioni thread "non sicure"?
Sono piuttosto nuovo al threading (beh, ho usato il threading per molti anni, ma solo forme molto semplici di esso). Ora mi trovo di fronte alla sfida di scrivere implementazioni parallele di alcuni algoritmi e i thread devono lavorare sugli stessi dati. Si consideri il seguente errore newbie:
const
N = 2;
var
value: integer = 0;
function ThreadFunc(Parameter: Pointer): integer;
var
i: Integer;
begin
for i := 1 to 10000000 do
inc(value);
result := 0;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
threads: array[0..N - 1] of THandle;
i: Integer;
dummy: cardinal;
begin
for i := 0 to N - 1 do
threads[i] := BeginThread(nil, 0, @ThreadFunc, nil, 0, dummy);
if WaitForMultipleObjects(N, @threads[0], true, INFINITE) = WAIT_FAILED then
RaiseLastOSError;
ShowMessage(IntToStr(value));
end;
Un principiante potrebbe aspettare il codice qui sopra per visualizzare il messaggio 20000000
. Infatti, il primo value
è uguale a 0
e quindi lo inc
è 20000000
volte. Tuttavia, poiché la procedura inc
non è "atomica", i due thread saranno in conflitto (suppongo che lo inc
faccia tre cose: legge, incrementa e salva), quindi molti dei numeri inc
verranno effettivamente persi '. Un valore tipico che ottengo dal codice sopra riportato è 10030423
.
La soluzione più semplice è utilizzare InterlockedIncrement
anziché Inc
(che sarà molto più lento in questo esempio sciocco, ma non è questo il punto). Un'altra soluzione consiste nel posizionare inc
all'interno di una sezione critica (sì, anche in questo esempio sciocco sarà molto lento).
Ora, nella maggior parte degli algoritmi reali, i conflitti non sono così comuni. In realtà, potrebbero essere molto rari. Uno dei miei algoritmi crea DLA fractals e una delle variabili che ho ogni ora e poi il numero di particelle adsorbite è I inc
. I conflitti qui sono molto rari e, cosa più importante, non mi interessa davvero se la variabile riassume 20000000, 20000008, 20000319 o 19999496. Quindi, è allettante non utilizzare InterlockedIncrement
o sezioni critiche, poiché essi solo gonfia il codice e lo rende (marginalmente) più lento a no (per quanto posso vedere) beneficio.
Tuttavia, la mia domanda è: possono esserci più gravi conseguenze dei conflitti rispetto a un valore leggermente "errato" della variabile incrementale? Il programma può bloccarsi, per esempio?
Certo, questa domanda potrebbe sembrare sciocco, perché, dopo tutto, il costo di utilizzo InterlockedIncrement
invece di inc
è piuttosto bassa (in molti casi, ma non tutti!), E quindi è (forse) stupido non per giocare al sicuro. Ma penso anche che sarebbe bello sapere come funziona davvero a livello teorico, quindi continuo a pensare che questa domanda sia molto interessante.
Immagino che il downvote sia stato causato dal titolo umoristico. –