Synchronize vraiment threadsafe ?
Bonjour à tous !
Lorsque dans un thread (plusieurs instances) on a un code comme celui-ci :
Code:
1 2 3 4 5 6
| Synchronize(procedure
begin
inc(SyncCount);
// SyncCount = 1 ?
Dec(SyncCount);
end); |
on s'attend à ce que SyncCount soit toujours à 1, que l'appel en cours se termine avant la prochaine synchronisation.
Sauf que si dans la procédure synchronisée on procède à la destruction d'un TThread, ce dernier attend la fin effective de sa tâche par un WaitFor qui relance un CheckSynchronize !
Résultat, un deuxième thread peut être autorisé à accéder au principal alors que la synchronisation précédente n'est pas terminée (TMonitor mal placé/utilisé ?) !
Voici une démo minimale pour illustrer cela. Ne la laissez pas tourner pendant des heures, le nombre de threads augmentant rapidement. Ctr+F2 pour aborter.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TWorkerThread = class(TThread)
protected
procedure Execute; override;
end;
TSyncThread = class(TThread)
protected
procedure Execute; override;
end;
TForm1 = class(TForm)
Label1: TLabel;
procedure FormCreate(Sender: TObject);
public
WorkerThread :TWorkerThread;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var SyncCount :integer;
{ TWorkerThread }
procedure TWorkerThread.Execute;
begin
while not Terminated do
Sleep(1000);
end;
{ TSyncThread }
procedure TSyncThread.Execute;
begin
while not Terminated do
Synchronize(procedure
begin
inc(SyncCount);
try
Form1.Label1.Caption := 'SyncCount = ' +SyncCount.ToString;
Form1.Label1.Update;
FreeAndNil(Form1.WorkerThread);
Form1.WorkerThread := TWorkerThread.Create;
finally
Dec(SyncCount);
end;
end);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
WorkerThread := TWorkerThread.Create;
TSyncThread.Create;
TSyncThread.Create;
TSyncThread.Create;
TSyncThread.Create;
end;
end. |
Pour retrouver le comportement logique, il faut englober Synchronize dans une section critique :aie:
Je suis en 10.4.2, est-ce que quelque sur un Delphi plus ancien observe le même comportement ou est-ce une régression ?