TStringList线程安全吗?

时间:2016-08-06 17:48:53

标签: multithreading delphi tstringlist

没有任何形式的同步,从TStringList读取数据是否可以?例如,与主线程同步。

示例代码

var MyStringList:TStringList; //declared globally

procedure TForm1.JvThread1Execute(Sender: TObject; Params: Pointer);
var x:integer;
begin

   for x:=0 to MaxInt do MyStringList.Add(FloatToStr(Random));  

end;


procedure TForm1.ButtonClick(Sender: TObject);
var x:integer;
   SumOfRandomNumbers:double;
begin

  for x:=0 to MyStringList.Count-1 do
    SumOfRandomNumbers:=SumOfRandomNumbers+StrToFloat(MyStringList.Strings[x]);

end;

或者我应该使用EnterCiticalSection保护对MyStringList的访问

var MyStringList:TStringList; //declared globally

procedure TForm1.JvThread1Execute(Sender: TObject; Params: Pointer);
var x:integer;
begin

   for x:=0 to MaxInt do 
   begin
     EnterCriticalSection(MySemaphore); 
     MyStringList.Add(FloatToStr(Random));  
     LeaveCriticalSection(MySemaphore);
   end;

end;


procedure TForm1.ButtonClick(Sender: TObject);
var x:integer;
   SumOfRandomNumbers:double;
begin

  for x:=0 to MyStringList.Count-1 do
  begin

     EnterCriticalSection(MySemaphore);
     SumOfRandomNumbers:=SumOfRandomNumbers+StrToFloat(MyStringList.Strings[x]);
     LeaveCriticalSection(MySemaphore);

  end;

end;

2 个答案:

答案 0 :(得分:16)

首先,TStringList不是线程安全的 其次,尝试制作它对于低级容器来说是一个糟糕的想法,在绝大多数情况下,它不会在多个线程之间共享。
第三,你提出的使线程安全的天真代码是不够的。它远远没有使它真正的线程安全 - 这是尝试这样做的一般问题的一部分。

在你的问题文本中,你问:

  

没有任何形式的同步从TStringList读取数据是否可以?

是的,没关系。事实上,这是首选,因为它更有效。

然而 ,如果跨线程共享数据,则可能会遇到问题。这就是为什么你应该最小化跨线程共享的数据量(不仅仅是字符串列表)。如果您需要分享数据,请以适当的控制方式进行。

扩展第3点

您的代码不是线程安全的原因是它无法保护您的数据免受共享访问的 所有 。这是多线程开发中常见的误解:“我只需要使用锁包装某些操作,一切都会好的。

关键是,如果您的列表是共享的,那么您就是:

  • 分享代表容器的结构。
  • AND 您正在共享数据成员(实际字符串)。
  • 在处理字符串时,这更进了一步,因为Delphi管理字符串的方式意味着它们可以与应用程序完全不同的区域中的相同值的其他字符串共享(通过内部引用计数)。

虽然您提出的锁定策略可能可能适合您的当前要求,但它远非一般的线程安全。

结论

如果您想编写线程安全的代码, onus就在您上:

  • 了解数据访问路径。
  • 最小化线程之间的共享(到目前为止最好的降压)。
  • 并实施安全共享数据的最佳策略(其中有许多选项,并且无论如何都不能保证锁定最佳)。

旁注

我之前说过,你的锁定技术只有“可能适合你当前的要求”,因为我不相信你真的给出了关于你 真实的指示 要求。如果你有,那么确实需要注意以下

在您提供的代码中,使TStringList“线程安全”绝对没有任何好处。您在循环中填充列表,并在第二个循环中读取值。您完全没有使用数据并发

您的代码最接近多线程的是:从主线程处理两个循环以避免阻止UI 是个好主意。在这种情况下,后台线程应 NOT 共享其TStringList实例。并且可以简单地与主线程同步以报告结果(以及可能的进度更新)。

通过共享不共享 的数据,您可以完全绕过对锁的需求。它们将是不必要的开销。而且你很高兴TStringList 没有拥有内置的“线程安全”机制。

答案 1 :(得分:1)

不,不是。 TStringList内部没有机制可锁定,例如.Add().GetStrings()

不幸的是,没有像TThreadList那样内置的东西,它是TList的线程安全包装器。但是你可以自己轻松地构建它。

以下是TStringList的同步装饰器的一个简单示例,其中我介绍了Add()的情况:

TThreadStringList = class
private
  FStringList: TStringList;
  FCriticalSection: TRtlCriticalSection;
  // ...
public 
  function Add(const S: string): Integer;
  // ...
end;

// ...

TThreadStringList.Add(const S: string): Integer;
begin
  EnterCriticalSection(FCriticalSection);
  try
    Result:= Add(S);
  finally
    LeaveCriticalSection(FCriticalSection);
  end;
end;

将其应用于您需要的所有其他方法应该很容易。

请记住,您必须初始化关键部分,然后才能使用,然后删除。< / p>