首页 新闻 会员 周边 捐助

Delphi多线程问题

1
悬赏园豆:200 [已解决问题] 解决于 2011-12-19 14:56

多线程读取TStringlist的问题,把一个大文件加载到TStringList中,开五个线程分别读其中一部分。文件中是多行数据,每行是空格间隔的8个浮点数,线程将这些数读出来,存到数组里面。我搞了好久,开5给线程耗时和1个线程没什么区别。大家帮我看看问题出在哪里了,谢谢大家!

线程代码: 

type   TReadTStringList = class(TThread)  

private     m_arrayindex, m_startlineno, m_endlineno : Integer;     m_btime,m_etime : TDateTime;  

procedure TReadTStringList.SetResult;

begin  

Form1.threadstatus[m_arrayindex] := 1;

end;

constructor TReadTStringList.Create(carrayindex,cstartlineno,cendlineno:Integer); begin  

inherited Create(false);  

 FreeOnTerminate := true;   m_arrayindex := carrayindex;  

 m_startlineno := cstartlineno;   m_endlineno := cendlineno;

end;

procedure TReadTStringList.Readlines();

var   i,j,cpartscount,cindex,cpos : Integer;  

splitor, cstr,cpartstr, xstr : String;  

cvalue : Double;

begin  

m_btime := Now;   splitor := ' ';   i := m_startlineno;   cindex := 0;  

while i <= m_endlineno do   begin    

cstr := Form1.gridfilelist[i];    

cstr := Trim(cstr);    

while cstr <> '' do     begin      

cpos := pos(splitor, cstr);      

 if cpos = 0 then         cpartstr := cstr      

else         cpartstr := copy(cstr, 1, cpos - 1);      

 cvalue := StrToFloatDef(cpartstr,0) ;      

cstr := copy(cstr, Length(cpartstr) + Length(splitor) + 1, Length(cstr) - Length(splitor) -1);      

cstr := TrimLeft(cstr);      

Form1.arraylist[m_arrayindex,cindex] := cvalue;      

Inc(cindex);    

end;  // while    

Inc(i);  

end; //while;  

 m_etime := Now;

 end;

 procedure TReadTStringList.Execute;

begin  

Readlines;  

Synchronize(SetResult);

end;

end.

 

下面是主线程部分:

datafile := ExtractFilePath(Application.Exename) + '1.txt';

 gridfilelist := TStringList.Create;  

gridfilelist.LoadFromFile(datafile);  

  linecount :=  gridfilelist.Count;

  threadlinecount := Trunc(linecount / 5);  

lastthreadlinecount := linecount - threadlinecount * 4;

  threadstatus[0] := 0;   threadstatus[1] := 0;   threadstatus[2] := 0;   threadstatus[3] := 0;   threadstatus[4] := 0;

  if threadlinecount >=  lastthreadlinecount then    

SetLength(arraylist,5, threadlinecount * 8)  

else    

SetLength(arraylist,5, lastthreadlinecount * 8);

  btime := Now;

  readthread1 := TReadTStringList.Create(0,0,threadlinecount);

  readthread2 := TReadTStringList.Create(1,threadlinecount+1,2 * threadlinecount);

  readthread3 := TReadTStringList.Create(2,2 * threadlinecount + 1, 3* threadlinecount);

  readthread4 := TReadTStringList.Create(3,3 * threadlinecount + 1, 4* threadlinecount);

  readthread5 := TReadTStringList.Create(4,4 * threadlinecount + 1, linecount-1);

  endthreadcount := 0;  

while endthreadcount < 5 do   begin    

endthreadcount := 0;    

for i := 0 to 4 do     begin      

 if threadstatus[i] = 1 then        

endthreadcount := endthreadcount + 1;    

end;    

Application.ProcessMessages;   end;

  ctime := Now;

  Memo1.Lines.Add('读取文件耗时:' + FloatToStr( MilliSecondsBetween(btime, ctime) / 1000)) ;

cuishuyue的主页 cuishuyue | 初学一级 | 园豆:37
提问于:2011-12-12 20:25
< >
分享
最佳答案
0

在您的主线程中,下面的语句

while endthreadcount < 5 do   begin    

endthreadcount := 0;    

for i := 0 to 4 do     begin      

 if threadstatus[i] = 1 then        

endthreadcount := endthreadcount + 1;    

end;    

Application.ProcessMessages;   end;

这个循环应该占用了不少的CPU时间片;您不应该用设置全局变量标志的方法来标志程序的结束;应该建立一个等待线程,来创建和启动这5个工作线程,然后这个等待线程调用 WaitForMultipleObjects Api 来等待工作线程数组的结束,结束后修改主界面的信息。主线程只启动这个等待线程。

当然, 工作线程也没必要调用Synchronize(SetResult), SetResult也是不必要的。

 这里的多线程根本没有涉及磁盘操作,因为在主线程开头就已经把整个文件装到gridfilelist。2楼根本没看清代码。您要比较的应该不包括这个读文件的操作吧。
 
如果英文可以的话,看看这篇文章 http://www.delphicorner.f9.co.uk/articles/op1.htm
收获园豆:180
ChatinCode | 老鸟四级 |园豆:2272 | 2011-12-13 11:48

我比较的不包括文件操作。如果像您说的:开一个等待线程,这个线程来创建和等待这5个线程。我在主线程中还得等待这个“等待线程”工作完,因为下面的操作要用这些线程处理的结果。这个怎么处理妥当呢?

cuishuyue | 园豆:37 (初学一级) | 2011-12-14 12:36

@cuishuyue: 主线程不需要等待等待线程的,等待线程WaitForMultipleObjects返回后就直接计算耗用的时间,然后用Syncronize更新主线程的UI控件,你看看我给的那个连接的最后一个连接的代码就会明白了。

ChatinCode | 园豆:2272 (老鸟四级) | 2011-12-14 16:14

@cuishuyue: 你把有些代码移到那个线程中就行了。主线程应该只有跟UI有关的操作,不要掺杂其他东西。

ChatinCode | 园豆:2272 (老鸟四级) | 2011-12-14 16:18

在我的电脑上,开5个线程,比开1个线程的消耗时间长多了。假如多开线程能够实现的话,不用买高性能配件,直接多开几个线程,运算时间就降下来了。以前学生的时候,好像只听说过算法上有空间换时间的说法。

ChatinCode | 园豆:2272 (老鸟四级) | 2011-12-16 11:06
 1 unit ParseLines;
2
3 interface
4
5 uses
6 Classes,SysUtils,DateUtils,ComCtrls;
7
8 type
9 TLinesParser = class(TThread)
10 private
11 { Private declarations }
12 m_StartLineNo, m_LineCount: Integer;
13 protected
14 procedure Execute; override;
15 public
16 constructor Create(StartLineNo, LineCount:Integer);
17 end;
18
19 implementation
20
21 uses
22 UnitCommon,StrUtils;
23
24 constructor TLinesParser.Create(StartLineNo, LineCount:Integer);
25 begin
26 m_StartLineNo := StartLineNo;
27 m_LineCount := LineCount;
28 FreeOnTerminate := true;
29 inherited Create(false);
30 end;
31
32 procedure TLinesParser.Execute;
33 var
34 i, j, nPos: Integer;
35 s: string;
36 begin
37 for i := m_StartLineNo to m_StartLineNo+m_LineCount-1 do begin
38 s := Lines[i];
39 j := 0;
40 while j < ColumnCount do
41 begin
42 nPos := Pos(NumericSeparator,s);
43 TryStrToFloat(LeftBStr(s,nPos), NumericValues[i,j]);
44 s := RightBStr(s, Length(s)-nPos);
45 Inc(j);
46 end;
47 end;
48 end;
49 end.

上面是ParseLines的内容;

原来文件可读性较差,我改写了下成为上面这个样子。

 1 unit Unit1;
2
3 interface
4
5 uses
6 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
7 Dialogs, StdCtrls, DateUtils ;
8
9 type
10 TForm1 = class(TForm)
11 btn5Thread: TButton;
12 Memo1: TMemo;
13 btn1Thread: TButton;
14 Memo2: TMemo;
15 procedure btn5ThreadClick(Sender: TObject);
16 procedure btn1ThreadClick(Sender: TObject);
17 private
18 ElapsedTime: Double;
19 procedure LoadFromFile;
20 public
21 { Public declarations }
22 end;
23
24 var
25 Form1: TForm1;
26
27 implementation
28
29 uses
30 UnitCommon, ParseLines;
31 {$R *.dfm}
32
33 procedure TForm1.LoadFromFile;
34 var
35 DataFilePath: string;
36 StartTime: TDateTime;
37 LineList: TStrings;
38 i, LineCount: Integer;
39 begin
40 StartTime := Now;
41 DataFilePath := ExtractFilePath(Application.ExeName) + '1.txt';
42 LineList := TStringList.Create;
43 LineList.LoadFromFile(DataFilePath);
44 LineCount := LineList.Count;
45 SetLength(Lines, LineCount);
46 For i := 0 to LineCount-1 do begin
47 Lines[i] := LineList[i];
48 end;
49 SetLength(NumericValues, LineCount, ColumnCount);
50 ElapsedTime := MilliSecondsBetween(StartTime, Now)/1000;
51 end;
52
53 procedure TForm1.btn1ThreadClick(Sender: TObject);
54 var
55 StartTime: TDateTime;
56 ParseThread: TThread;
57 begin
58 LoadFromFile;
59 Memo2.Lines.Add('加载文件耗时:'+FloatToStr(ElapsedTime));
60
61 StartTime := Now;
62 ParseThread := TLinesParser.Create(0, Length(Lines));
63 WaitForSingleObject(ParseThread.Handle,INFINITE);
64 ElapsedTime := MilliSecondsBetween(StartTime, Now)/1000;
65 Memo2.Lines.Add('分析文件耗时:'+FloatToStr(ElapsedTime));
66 end;
67
68 procedure TForm1.btn5ThreadClick(Sender: TObject);
69 const
70 ThreadCount: Integer = 5;
71 var
72 LineCount, PerThreadLineCount, StartLineNo: Integer;
73 StartTime: TDateTime;
74 ThreadHandles: Array[1..5] of THandle;
75 i: Integer;
76 begin
77 LoadFromFile;
78 Memo1.Lines.Add('加载文件耗时:'+FloatToStr(ElapsedTime));
79
80 StartTime := Now;
81 LineCount := Length(Lines);
82 PerThreadLineCount := LineCount div ThreadCount;
83 StartLineNo := 0;
84 for i:= 1 to ThreadCount do begin
85 if i <> ThreadCount then
86 ThreadHandles[i] :=
87 TLinesParser.Create(StartLineNo, PerThreadLineCount).Handle
88 else
89 ThreadHandles[i] :=
90 TLinesParser.Create(StartLineNo, LineCount-StartLineNo).Handle;
91 StartLineNo := StartLineNo + PerThreadLineCount;
92 end;
93 WaitForMultipleObjects(ThreadCount, @ThreadHandles, True, INFINITE);
94 ElapsedTime := MilliSecondsBetween(StartTime, Now)/1000;
95 Memo1.Lines.Add('分析文件耗时:'+FloatToStr(ElapsedTime));
96 end;

上面是Unit1的内容;

 1 unit UnitCommon;
2
3 interface
4 uses
5 Classes;
6 const
7 NumericSeparator = '';
8 ColumnCount: Integer = 8;
9 var
10 ThreadCount: Integer;
11 Lines: Array of string;
12 NumericValues: Array of Array of Double;
13 implementation
14
15 end.

上面是UnitCommon的内容;



 

ChatinCode | 园豆:2272 (老鸟四级) | 2011-12-16 12:01
其他回答(2)
0

因为你用了同步函数Synchronize

这玩意儿,同时只有一个线程操作,开100个也是1个的速度。

收获园豆:10
4lan | 园豆:220 (菜鸟二级) | 2011-12-13 10:28
0

delphi不太懂,看1楼说的好像是对的。不过还有一个问题你没有注意到,就是N个线程操作N个文件,未必就比1个线程操作N个文件快很多,因为磁盘的IO在这种情况下是性能瓶颈所在(你可以想象只有一个磁头在读数据,但是N个线程都争着用,不比一个线程用强)

收获园豆:10
水牛刀刀 | 园豆:6350 (大侠五级) | 2011-12-13 10:47
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册