多线程读取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)) ;
在您的主线程中,下面的语句
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也是不必要的。
我比较的不包括文件操作。如果像您说的:开一个等待线程,这个线程来创建和等待这5个线程。我在主线程中还得等待这个“等待线程”工作完,因为下面的操作要用这些线程处理的结果。这个怎么处理妥当呢?
@cuishuyue: 主线程不需要等待等待线程的,等待线程WaitForMultipleObjects返回后就直接计算耗用的时间,然后用Syncronize更新主线程的UI控件,你看看我给的那个连接的最后一个连接的代码就会明白了。
@cuishuyue: 你把有些代码移到那个线程中就行了。主线程应该只有跟UI有关的操作,不要掺杂其他东西。
在我的电脑上,开5个线程,比开1个线程的消耗时间长多了。假如多开线程能够实现的话,不用买高性能配件,直接多开几个线程,运算时间就降下来了。以前学生的时候,好像只听说过算法上有空间换时间的说法。
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的内容;
因为你用了同步函数Synchronize
这玩意儿,同时只有一个线程操作,开100个也是1个的速度。
delphi不太懂,看1楼说的好像是对的。不过还有一个问题你没有注意到,就是N个线程操作N个文件,未必就比1个线程操作N个文件快很多,因为磁盘的IO在这种情况下是性能瓶颈所在(你可以想象只有一个磁头在读数据,但是N个线程都争着用,不比一个线程用强)