关于如何在线程中等待数据并将其写入文件的最佳实践?

时间:2012-03-19 20:57:25

标签: multithreading delphi thread-safety delphi-2010

我正在编写多线程应用程序。我有两个主题。 On用于将一些数据从设备传输到全局数据缓冲区,其次用于将这些数据写入文件。

从设备到缓冲区的数据是异步传输的。第二个线程的目的应该是等待指定数量的数据写入主数据缓冲区,最后将其写入文件。

第一个线程在DLL中,第二个在主应用程序中。我暂时用事件来解决这个问题。第一个线程将数据从设备传输到主数据缓冲区并对数据进行计数,当传输指定数量的数据时,它会设置一个事件。第二个等待事件被发出信号,当它运行时,它运行一些数据存储的代码。很简单,因为它工作。

  Thread1.Execute:

  var DataCount, TransferedData: Integer;

  DataCounter := 0;
  while not Terminted do
  begin
    TransferData(@pData, TransferedData);
    Inc(DataCounter, TransferedData)
    if DataCounter >= DataCountToNotify then SetEvent(hDataCount);
  end;



  Thread2.Execute:

  hndlArr[0] := hDataCount;
  hndlArr[1] := hTerminateEvent;

  while (not Terminated) do
  begin
    wRes := WaitForMultipleObjects(HandlesCount, Addr(hndlArr), false, 60000);
    case wRes of
      WAIT_OBJECT_0:
        begin
          Synchronize(WriteBuffer);                   // call it from main thread
          ResetEvent(hndlArr[0]);
        end;
      WAIT_OBJECT_0 + 1:
        begin
          ResetEvent(hTerminateEvent);
          break;
        end;
      WAIT_TIMEOUT:  Break;
    end;
  end;

现在我想让第二个线程更加独立......所以我可以创建第二个线程的多个实例,而且我不需要等待第一个线程。我想将计算部分代码的数据从第一个线程转移到第二个线程,因此我不再需要在第一个线程中进行数据计数。第一个将仅用于数据传输目的。

我想使用第二个作为数据计数器并存储数据。但现在我必须循环并不断手动检查指定的数据量。如果我有while循环,我将不得不添加一些睡眠,所以第二个线程不会降低计算机性能,但我不知道应该睡多久,而firts线程中的数据传输速度不是恒定的,因此计数速度第二个线程会有所不同。

我猜这个代码示例不好:

  Thread2.Execute:
  var DataCount: Integer;
  DataIdx1 := GetCurrentDataIdx; 
  while (not Terminated) do
  begin     
    if (GetCurrentDataIdx - DataIdx1) >= DataCountToNotify then 
    begin
      Synchronize(WriteBuffer);
      DataIdx1 := GetCurrentIdx;
    end;
    sleep(???);
  end;

所以我的问题是什么是解决数据计数问题并将其存储在第二个线程中的最佳方法?您有什么经验和建议?

2 个答案:

答案 0 :(得分:5)

你有一些问题。 @LU RD已经指出了一个 - 不要同步不需要同步的东西。目前还不清楚'WriteBuffer'是做什么的,但我使用的文件系统和所有数据库都可以让一个线程打开一个文件/表并写入它们。

您的缓冲系统可能会引起一些注意。是否有一些“指定数据量”或者这是一个允许懒惰写作的名义数字?

通常,生产者和消费者线程在队列上交换多个缓冲区指针,因此避免共享任何单个缓冲区。鉴于这是一个DLL,因此内存管理调用可能会有问题,我可能会通过在启动时创建一个缓冲池来为系统传输数据来避免它们。我会使用缓冲类而不仅仅是指向内存的指针,但这并不是绝对必需的(更简单/更灵活/更安全)。

Sleep()循环是一种非常糟糕的线程间通信方式。 Sleep()确实有它的用途,但这不是其中之一。 Delphi / Windows有很多同步机制 - 事件,信号量,互斥量等 - 不需要这样的轮询。

LU RD还提到了必须保留其顺序的数据并行处理的问题。这通常需要另一个线程,列表样式集合和序列号。在你让线程间通信运行良好之前我不会尝试。

答案 1 :(得分:2)

如果您想避免在第二个帖子中调用Sleep(),请使用像TSimpleEvent这样的等待计时器。

设置睡眠时间以处理所有时间条件。 使用此方案而不是普通Sleep()是有利的,因为等待计时器不会使线程进入深度睡眠状态。

要处置该线程,请参阅代码中的注释。

var
  FEvent: TSimpleEvent;
  FSleepTime: Integer = 100;  // Short enough to handle all cases

Constructor TThread2.Create;
begin
  Inherited Create( False);
  FEvent := TSimpleEvent.Create;
  Self.FreeOnTerminate := True;
end;

procedure TThread2.Execute;
var 
  DataCount: Integer;
begin
  DataIdx1 := GetCurrentDataIdx; 
  while (fEvent.WaitFor(FSleepTime) = wrTimeout) do
  begin
    if Terminated then
      break;
    // Do your work
    if (GetCurrentDataIdx - DataIdx1) >= DataCountToNotify then 
    begin
      // Write data to buffer
      DataIdx1 := GetCurrentIdx;
    end;
  end;
end;

// To stop the thread gracefully, call this instead of Terminate or override the DoTerminate
procedure TThread2.SetTerminateFlag;
begin
  FEvent.SetEvent;
end;