看《Delphi 7 高级应用开发教程》的朋友,你们好。(没在看也没关系,呵呵)
第一章讲多线程技术及其应用,里面讲到互斥对象,书中给出的实现代码如下
1 unit Unit1;
2
3 interface
4
5 uses
6 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
7 Dialogs, StdCtrls;
8
9 const
10 MaxSize = 20;
11
12 type
13 TThreadTest = class(TThread)
14 protected
15 procedure Execute; override;
16 end;
17
18 TForm1 = class(TForm)
19 Memo1: TMemo;
20 Memo2: TMemo;
21 Button1: TButton;
22 procedure FormCreate(Sender: TObject);
23 procedure FormDestroy(Sender: TObject);
24 procedure Button1Click(Sender: TObject);
25 private
26 { Private declarations }
27 Thread1, Thread2 : TThreadTest;
28 procedure Thread1Done(Sender : TObject);
29 procedure Thread2Done(Sender : TObject);
30
31 public
32 { Public declarations }
33 end;
34
35 var
36 Form1: TForm1;
37 HMutex : THandle = 0;
38 IncNum : Integer;
39 GlobalData : Array[1..MaxSize] of Integer;
40
41 implementation
42
43 {$R *.dfm}
44
45 procedure TThreadTest.Execute;
46 var
47 i : Integer;
48 WaitReturn : DWord;
49 begin
50 FreeOnTerminate := True;
51 WaitReturn := WaitForSingleObject(HMutex, INFINITE);
52 if WaitReturn = Wait_Object_0 then
53 begin
54 for i := 1 to MaxSize do
55 begin
56 Inc(IncNum);
57 GlobalData[i] := IncNum;
58 Sleep(10);
59 end;
60 end;
61 end;
62
63 procedure TForm1.Thread1Done(Sender : TObject);
64 var
65 i : Integer;
66 begin
67 Memo1.Lines.Clear;
68 for i:= 1 to MaxSize do
69 begin
70 Memo1.Lines.Add(IntToStr(GlobalData[i]));
71 end;
72 ReleaseMutex(HMutex);
73 end;
74
75 procedure TForm1.Thread2Done(Sender : TObject);
76 var
77 i : Integer;
78 begin
79 Memo2.Lines.Clear;
80 for i:= 1 to MaxSize do
81 begin
82 Memo2.Lines.Add(IntToStr(GlobalData[i]));
83 end;
84 ReleaseMutex(HMutex);
85 end;
86
87 procedure TForm1.FormCreate(Sender: TObject);
88 begin
89 HMutex := CreateMutex(nil, False, nil);
90 end;
91
92 procedure TForm1.FormDestroy(Sender: TObject);
93 begin
94 CloseHandle(HMutex);
95 end;
96
97 procedure TForm1.Button1Click(Sender: TObject);
98 begin
99 Thread1 := TThreadTest.Create(False);
100 Thread1.OnTerminate := Thread1Done;
101 Thread2 := TThreadTest.Create(False);
102 Thread2.OnTerminate := Thread2Done;
103 end;
104
105 end.
2
3 interface
4
5 uses
6 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
7 Dialogs, StdCtrls;
8
9 const
10 MaxSize = 20;
11
12 type
13 TThreadTest = class(TThread)
14 protected
15 procedure Execute; override;
16 end;
17
18 TForm1 = class(TForm)
19 Memo1: TMemo;
20 Memo2: TMemo;
21 Button1: TButton;
22 procedure FormCreate(Sender: TObject);
23 procedure FormDestroy(Sender: TObject);
24 procedure Button1Click(Sender: TObject);
25 private
26 { Private declarations }
27 Thread1, Thread2 : TThreadTest;
28 procedure Thread1Done(Sender : TObject);
29 procedure Thread2Done(Sender : TObject);
30
31 public
32 { Public declarations }
33 end;
34
35 var
36 Form1: TForm1;
37 HMutex : THandle = 0;
38 IncNum : Integer;
39 GlobalData : Array[1..MaxSize] of Integer;
40
41 implementation
42
43 {$R *.dfm}
44
45 procedure TThreadTest.Execute;
46 var
47 i : Integer;
48 WaitReturn : DWord;
49 begin
50 FreeOnTerminate := True;
51 WaitReturn := WaitForSingleObject(HMutex, INFINITE);
52 if WaitReturn = Wait_Object_0 then
53 begin
54 for i := 1 to MaxSize do
55 begin
56 Inc(IncNum);
57 GlobalData[i] := IncNum;
58 Sleep(10);
59 end;
60 end;
61 end;
62
63 procedure TForm1.Thread1Done(Sender : TObject);
64 var
65 i : Integer;
66 begin
67 Memo1.Lines.Clear;
68 for i:= 1 to MaxSize do
69 begin
70 Memo1.Lines.Add(IntToStr(GlobalData[i]));
71 end;
72 ReleaseMutex(HMutex);
73 end;
74
75 procedure TForm1.Thread2Done(Sender : TObject);
76 var
77 i : Integer;
78 begin
79 Memo2.Lines.Clear;
80 for i:= 1 to MaxSize do
81 begin
82 Memo2.Lines.Add(IntToStr(GlobalData[i]));
83 end;
84 ReleaseMutex(HMutex);
85 end;
86
87 procedure TForm1.FormCreate(Sender: TObject);
88 begin
89 HMutex := CreateMutex(nil, False, nil);
90 end;
91
92 procedure TForm1.FormDestroy(Sender: TObject);
93 begin
94 CloseHandle(HMutex);
95 end;
96
97 procedure TForm1.Button1Click(Sender: TObject);
98 begin
99 Thread1 := TThreadTest.Create(False);
100 Thread1.OnTerminate := Thread1Done;
101 Thread2 := TThreadTest.Create(False);
102 Thread2.OnTerminate := Thread2Done;
103 end;
104
105 end.
上面的执行结果如下图:
没有达到预想的效果,预想的效果如下图:
这就说明了,没有达到线程同步的效果,那么是不是互斥对象的效果也没有达到呢?
其实互斥对象的效果已经到达了,只不过Thread2在执行Execute的时候WaitForSingleObject函数返回的是WAIT_ABANDONED
所以没有执行下面的循环,GlobalData数组仍然是Thread1执行后的结果。
WAIT_ABANDONED 的含义如下
指定的对象是互斥对象,并且拥有这个互斥对象的线程在没有释放此对象之前就已终止。此时就称互斥对象被抛弃。这种情况下,这个互斥对象归当前线程所有,并将它设为不发信号状态。
这就说明,建立的互斥对象在没有执行ReleaseMutex之前就被终止了。所以Thread2调用WaitForSingleObject函数的时候就返回WAIT_ABANDONED。
所以要线程正确同步就必须把ReleaseMutex函数放在Execute里面
修改如下
procedure TThreadTest.Execute;
var
i : Integer;
WaitReturn : DWord;
begin
FreeOnTerminate := True;
WaitReturn := WaitForSingleObject(HMutex, INFINITE);
if WaitReturn = Wait_Object_0 then
begin
for i := 1 to MaxSize do
begin
Inc(IncNum);
GlobalData[i] := IncNum;
Sleep(10);
end;
end;
ReleaseMutex(HMutex);
end;
var
i : Integer;
WaitReturn : DWord;
begin
FreeOnTerminate := True;
WaitReturn := WaitForSingleObject(HMutex, INFINITE);
if WaitReturn = Wait_Object_0 then
begin
for i := 1 to MaxSize do
begin
Inc(IncNum);
GlobalData[i] := IncNum;
Sleep(10);
end;
end;
ReleaseMutex(HMutex);
end;
并且把Thread1Done过程和Thread2Done过程中的ReleasMutex删除
这样就达到了线程同步的效果。
不过,具体为什么互斥对象在Thread1线程中使用后,就被自动终止了,导致Thread2使用WaitForSingleObject函数返回WAIT_ABANDONED这一点。我仍不理解,还请大家多多指教