将程序转换为多线程,利用多核cpu

时间:2010-06-19 05:43:26

标签: delphi multithreading

我有一个简单的程序,只有一个程序。

Procedure TForm1.btnKeywrdTransClick(Sender: TObject);
Var
  i, ii             : integer;
  ch_word, zword, uy_word: widestring;
Begin
  TntListBox1.items.LoadFromFile('d:\new folder\chh.txt'); //Chinese 
  TntListBox2.items.LoadFromFile('d:\new folder\uyy.txt'); //Uyword
  TntListBox4.items.LoadFromFile(Edit3.text); //list of poi files
  For I := 0 To TntListBox4.items.Count - 1 do
  Begin
  TntListBox3.items.LoadFromFile(TntListBox4.Items[i]);
  zword := tntlistbox3.Items.Text;      //Poi
  For ii := 0 To TntListBox1.Items.count - 1 Do
  Begin
    loopz;
    ch_word := tntlistbox1.Items[ii];
    uy_word := ' ' + TntListBox2.items[ii] + ' ';
    zword := wideFastReplace(zword, ch_word, uy_word, [rfReplaceAll]); //fastest, and better for large text
  End;
  TntListBox3.Items.text := zword;
  TntListBox3.items.SaveToFile(TntListBox4.Items[i]);
end;
end;

现在我的新计算机有4个核心,正在使这个程序多线程会让它运行得更快(如果我使用4个线程,每个核心一个线程)? 我没有多线程的经验,我需要你的帮助 感谢。

ps:这是Loopz程序

Procedure loopz;
Var
  msg               : tmsg;
Begin
  While PeekMessage(Msg, 0, 0, 0, pm_Remove) Do
  Begin
    If Msg.Message = wm_Quit Then Halt(Msg.WParam);
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  End;
End;

更新1: 从答案,我要做

1 - 使用分析器查找最耗时的代码

2 - 尝试尽可能消除与gui相关的事情

3 - 使用线程。

我会报告回来。谢谢大家。

6 个答案:

答案 0 :(得分:14)

首先让算法尽可能有效,因为它当前的化身:停止使用TListBox来存储你的数据!!! (抱歉叫喊)用TStringList替换它们你会获得巨大性能提升。这是任何方式都需要的第一步,因为你不能使用来自多个线程的GUI对象(实际上你只能从“主”线程中使用它们)。当您将TListBox更改为TStringList 时,请为变量指定有意义的名称。我不知道这里有多少人发现你在ListBox4中存储了一个文件名列表,在ListBox3中加载每个文件,使用ListBox1作为“关键字列表”,将ListBox2作为“值列表”...真的,这是一个很大的混乱!以下是TStringList和专有名称的外观:

Procedure TForm1.btnKeywrdTransClick(Sender: TObject);
Var
  i, ii             : integer;
  ch_word, zword, uy_word: widestring;
  PoiFilesList:TStringList; // This is the list of files that need work
  PoiFile:TStringList; // This is the file I'm working on right now
  KeywordList, ValueList:TStringList; // I'll replace all keywords with corresponding values
Begin

  PoiFilesList := TStringList.Create;
  PoiFile := TStringList.Create;
  KeywordList := TStringList.Create;
  ValueList := TStringList.Create;

  try
    PoiFilesList.LoadFromFile(Edit3.text); //list of poi files
    KeywordList.LoadFromFile('d:\new folder\chh.txt'); //Chinese 
    ValueList.LoadFromFile('d:\new folder\uyy.txt'); //Uyword
    For I := 0 To PoiFilesList.Count - 1 do
    Begin
      PoiFile.LoadFromFile(PoiFilesList[i]);
      zword := PoiFile.Text;      //Poi
      For ii := 0 To KeywordList.count - 1 Do
      Begin
        ch_word := KeywordList[ii];
        uy_word := ' ' + ValueList[ii] + ' ';
        zword := wideFastReplace(zword, ch_word, uy_word, [rfReplaceAll]);
      End;
      PoiFile.text := zword;
      PoiFile.SaveToFile(PoiFilesList[i]);
    end;
  finally
    PoiFilesList.Free;
    PoiFile.Free;
    KeywordList.Free;
    ValueList.Free;
  end;
end;

如果你现在看一下代码,它显然是做什么的,而且很明显如何多线程 - 它。你有一个包含文件名的文本文件。您打开这些文件中的每一个,并用相应的值替换所有关键字。您将文件保存回磁盘。这很简单!将KeywordList和ValueList加载到内存中一次,将文件列表拆分为4个较小的列表,启动4个线程,每个线程使用自己的较小文件列表。

我不想编写代码的整个多线程变体,因为如果我自己编写它,你可能不会理解它是如何工作的。如果遇到麻烦,给它一个机会并寻求帮助。

答案 1 :(得分:4)

首先,您应该对代码进行概要分析,以查看从TntListBox读取是否会降低您的速度或者是否为WideFastReplace。但即使在此之前,删除'loopz'调用 - 它会让你最慢!你为什么要在这个循环中处理消息?

要找到瓶颈,只需将循环计时两次,但第二次注释掉WideFastReplace调用。 (并确保您只计时循环,而不是TntListBox3的赋值或保存到文件或从文件加载。)

当你知道什么在减慢你的速度时,请回报......

BTW,并行调用WideFastReplace几乎是不可能的,因为它始终在同一个源上运行。我没有看到任何有效的代码并行化方法。

可能的并行化方法:

  • 在适当的单词分隔符上分割zword(我假设你只是将单词而不是短语替换成N个字符串,其中N是核心数。)
  • 并行地对这N个字符串中的每一个进行完全替换(所有搜索/替换对)。当然,你必须首先从TntListBoxes读取搜索/替换对到一些内部结构(TStringList就足够了),然后在所有N个线程中使用这个结构。
  • 将这些部分字符串连接在一起。

当然,如果WideFastReplace不是代码的耗时部分,那么这样做是没有意义的。首先进行分析!

答案 2 :(得分:2)

看起来你正在与GUI元素接口。

99%的GUI代码必须从一个且只有一个线程接口。

如果您重构代码以在一系列线程中执行文本替换,在它们之间划分文本,然后让GUI线程将其放入列表框中,则可以提高性能。

请注意,创建和同步线程并不便宜。除非您有数千个条目要处理,否则您可能会通过添加线程来减慢您的程序。

答案 3 :(得分:1)

通过仅使用一个线程来完成整个过程,您将获得相当大的改进。有了这个,您可以完全省略loopz电话。

请注意,您应该使用例程中的本地TWideStringList实例替换TntListbox。

如果您对多线程有所了解,可以将工作拆分为多个线程。例如,可以通过在多个(例如3-4个)列表中拆分poi文件列表(listbox4)来完成此操作,每个线程一个。

答案 4 :(得分:1)

可以并行运行的操作受益于多任务处理 - 那些必须一个接一个地运行的操作不能。操作越大,效益越大。在您的过程中,您可以并行化文件加载(虽然我猜它们没有那么多元素)并且您可以并行化具有多个线程的替换操作,每个线程在不同的列表元素上运行。它的运行速度取决于文件大小。 我猜你在使用GUI元素存储数据而不是直接在内存结构上工作时有更多的速度,因为这意味着经常重新绘制控件,这是一项昂贵的操作。

答案 5 :(得分:-1)

这是你的答案 1.如果可以,请不要等到用户单击以对操作做出反应。像在formcreate之前那样做   把它们放入包装对象中   在线程下运行它;完成后,将其标记为可以使用   当用户单击该操作时,请检查标记。如果没有完成
  然后做一个循环并等待

之类的东西
btnKeywrdTrans.Enabled := False;
while not wrapper.done do
begin
  Sleep(500);
  Application.Processmessages;
end; 
..... your further logic
btnKeywrdTrans.Enabled := True;
  1. 将其替换为TStringList或TWideStringList
  2. 干杯 范