Delphi并行编程 - 多线程慢

use*_*126 3 delphi parallel-processing multithreading 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;
Run Code Online (Sandbox Code Playgroud)

图像已添加到队列中.

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

创建线程.

for i:=0 to 2 do
    TSaveThread.Create(false);
Run Code Online (Sandbox Code Playgroud)

问题是,这些线程无法保存所有这些图像.为什么?我怎样才能改进我的线程?

Delphi版本:Delphi XE2

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

Dav*_*nan 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.
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  1. Sleep通常应该避免.在你的情况下,如果最后一次拉动图像的尝试成功,那么睡觉就是自杀.不要那样做.您应该使用真正的线程队列.允许正确阻塞的一个等待同步对象.使用事件对象和您最喜欢的非线程队列自己制作一个实际上非常简单.
  2. 你在打电话的同时拿着锁TElement.Create(bmWebcam).这将阻碍扩展.分配TElement.Create(bmWebcam)给锁外的局部变量.然后分配给锁内的共享数据.

所以,你可以通过首先删除调用来检查这些想法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;
Run Code Online (Sandbox Code Playgroud)

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

  • 你应该能够自己做数学.我机器上3个线程的理论峰值吞吐量是每秒33帧.这危险地接近您需要达到的最小吞吐量.你糟糕的线程代码(睡眠,锁定持续时间太长)意味着你无法达到理论上的峰值.此外,磁盘IO上将缺乏扩展,尽管这不是主要的瓶颈. (2认同)