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

时间:2019-07-14 16:27:33

标签: multithreading delphi firemonkey

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

我声明了一个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;
....

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

1 个答案:

答案 0 :(得分: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

您可以改用TCriticalSection中的System.SyncObjs

var DataLock: TCriticalSection;

procedure ClearAll();
begin
  DataLock.Enter;
  try
    SetLength(Data, 0);
  finally
    DataLock.Leave;
  end;
end;

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

initialization
  DataLock := TCriticalSection.Create;

finalization  
  DataLock.Free;
end.

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