Delphi并行编程 - 多线程慢

时间:2012-12-12 13:17:13

标签: multithreading delphi parallel-processing delphi-xe2

美好的一天,

网络摄像头类每秒大约有30帧,所有这些帧都将保存在矢量(如队列)中。然后3个异步线程将读取队列,并将尝试执行其工作(以保存这些图像)。为什么队列溢出?所以问题是这些线程比网络摄像头慢。

Procedure TSaveThread.Execute;
begin
   while not terminated  do
   begin
      elElement:=NIL;

      EnterCriticalSection(CritSect);
         if iElementsLength>=0 then
         begin
            elElement:=vElements[iElementsLength];
            Dec(iElementsLength);
         end;
      LeaveCriticalSection(CritSect);

      if elElement<>NIL then
      begin
         JpegImg.Assign(elElement.bmWebcam) ;
         JpegImg.SaveToFile('Save\'+elElement.sTime+'.jpg') ;
         elElement.Free;
      end;

      Sleep(20);
   end;
end;

图片已添加到队列中。

//------------------------------------------------------------------------------
Procedure TWebcam.OnSave(Sender:TObject; bmWebcam:TBitmap);
begin
   EnterCriticalSection(CritSect);
      inc(iElementsLength);
      vElements[iElementsLength]:=TElement.Create(bmWebcam);
   LeaveCriticalSection(CritSect);
end;

创建线程。

for i:=0 to 2 do
    TSaveThread.Create(false);

问题是,这些线程无法保存所有这些图像。为什么?如何改进我的线程?

Delphi版本:Delphi XE2

网络摄像头框架尺寸:1280x760或960x600 这里的整个源代码:http://pastebin.com/8SekN4TE

2 个答案:

答案 0 :(得分:15)

我写了以下程序:

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, Vcl.Graphics, Vcl.Imaging.jpeg, 
  System.IOUtils, System.Diagnostics;

var
  i: Integer;
  bmp: TBitmap;
  jpeg: TJPEGImage;
  Stopwatch: TStopwatch;

begin
  bmp := TBitmap.Create;
  bmp.SetSize(1280, 760);
  jpeg := TJPEGImage.Create;
  Stopwatch := TStopwatch.StartNew;
  for i := 1 to 100 do begin
    jpeg.Assign(bmp);
    jpeg.SaveToFile('C:\desktop\temp\'+TPath.GetRandomFileName);
  end;
  Writeln(Stopwatch.ElapsedMilliseconds);
  Readln;
end.

它将1280x760像素位图转换为JPEG图像,然后保存到磁盘。它这样做了100次。在我的机器上,这需要9秒。这是每秒11幅图像的吞吐量。如果我跳过转换为JPEG步骤并直接保存位图,我可以获得每秒150张图像的吞吐量。很明显,转换为JPEG是一个瓶颈。

您正在寻找每秒30张图片。虽然多线程有帮助,但我怀疑你有四核机器。一个用于网络摄像头的处理器,三个用于保存。因此,如果您只有三个线程可用,那么您可能很难达到每秒30帧所需的吞吐量。我机器上的理论峰值是33.如果你没有达到每秒30帧,那么你的队列显然会溢出。

显而易见的结论是,您需要找到更快的JPEG转换库。我很确定存在这样的库。例如,我认为libjpeg应该快得多。

至于你现有的代码,有一些明显的缺陷:

    通常应避免使用
  1. Sleep。在你的情况下,如果最后一次拉动图像的尝试成功,那么睡觉就是自杀。不要那样做。您应该使用真正的线程队列。允许正确阻塞的一个等待同步对象。实际上,使用事件对象和您最喜欢的非线程队列来制作一个非常简单。
  2. 您在拨打TElement.Create(bmWebcam)时正握着锁。这将阻碍扩展。将TElement.Create(bmWebcam)分配给锁外部的局部变量。然后分配给锁内的共享数据。
  3. 因此,您可以首先删除对Sleep的调用,以检查这些提示。然后将TWebcam.OnSave更改为如下所示:

    Procedure TWebcam.OnSave(Sender:TObject; bmWebcam:TBitmap);
    var
      NewElement: TElement;
    begin
      NewElement := TElement.Create(bmWebcam);
      EnterCriticalSection(CritSect);
        inc(iElementsLength);
        vElements[iElementsLength] := NewElement;
      LeaveCriticalSection(CritSect);
    end;
    

    这些建议会有所帮助,但我认为你需要解决基本问题,即JPEG转换。

答案 1 :(得分:4)

MultiThreadding不会加速您的媒体(HardDrive)。

事实上,它可以通过并行写访问来减慢

首先,您必须测量您的媒体(硬盘)是否能够在不到33毫秒的时间内存储图像 - 因为每33.333毫秒您将从网络摄像头获得一个新图像。

如果没有,你不能期望这个运行。

你应该(和/或)

  • 使用更多硬盘(例如每个线程一个)
  • 使用更多缓存(例如缓存控制器)
  • 使用速度更快的硬盘(例如SSD)
  • 使用较小的图像(降低分辨率)
  • 删除一些图片

如果你需要快速,不要浪费时间

if elElement<>NIL then
  begin
     JpegImg.Assign(elElement.bmWebcam) ;
     JpegImg.SaveToFile('Save\' + elElement.sTime + '.jpg' );
     elElement.Free;
  end
else
  Sleep(20);

OTL无助于让您的媒体更快,但更清洁:o)

所以你应该看看