多线程时如何安全地访问和修改数组?

abd*_* ba 1 delphi multithreading firemonkey

我正在尝试array of record通过多个线程来操纵类型为type的变量,并且不确定执行的方法是否正确,或者是否有更好更好的方法?

我声明了一个boolean变量作为锁,当某个线程想要访问该数组时,它将等待直到锁关闭,然后激活该锁,完成后,将其解锁并让其他人可以访问。

在实现部分中声明了此代码

...
implementation

var Data : array of TData;
var Data_Lock:Boolean=false;

procedure Lock_Data();
begin
   while Data_Lock = True do
       sleep(1);
   Data_Lock := True;
end;

procedure UnLock_Data();
begin
    Data_Lock := False;
end;

procedure ClearAll();
begin
    Lock_Data();
    SetLength( Data, 0 );
    UnLock_Data();
end;
....
Run Code Online (Sandbox Code Playgroud)

整个项目仍未完成。现在看来这可行,但是我对这些东西如何在核心工作一无所知,如果两个线程在同一时间启动,是否会有问题?

Dal*_*kar 5

您的锁定方法不是线程安全的,并且不会保护您的数据。

对于多线程,您必须考虑到任何特定线程的执行都可以随时中断,并且另一个线程可以“跳入”并在它们之间访问某些变量。

这意味着以下情况是可能的(简化):

Data_Lock is False
Thread A enters Lock_Data()
Thread A checks Data_Lock -> False and skips the loop
Thread B enters Lock_Data()
Thread B checks Data_Lock -> False and skips the loop (Thread A didn't have the chance to set it to True yet)
Thread A continues -> sets Data_Lock to True and gains access to protected data
Thread B continues -> sets Data_lock to True and gains access to protected data while Thread A is still using that data
Run Code Online (Sandbox Code Playgroud)

您可以改用TCriticalSectionfrom System.SyncObjs

var DataLock: TCriticalSection;

procedure ClearAll();
begin
  DataLock.Enter;
  try
    SetLength(Data, 0);
  finally
    DataLock.Leave;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

由于TCriticalSection是一个类,因此您需要先创建DataLock实例,然后才能使用它,并且在不再需要它时需要释放它。例如,您可以在单元的初始化/完成部分中执行此操作。

initialization
  DataLock := TCriticalSection.Create;

finalization  
  DataLock.Free;
end.
Run Code Online (Sandbox Code Playgroud)

但是,更好的方法是将数据和关键部分包装在一个类中。