先看如下代码:
程序作用:初始化整数数组,升序排列,第一个线程先从1-128,第二个线程则重新初始化数组为128-256。
我们把最后一个线程(即第二个线程)的初始化结果显示在列表容器里。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure ThreadsDone(Sender: TObject);
public
{ Public declarations }
end;
TFooThread = class(TThread)
protected
procedure Execute; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
MaxSize = 128;
var
NextNumber : Integer = 0;
DoneFlag : Integer = 0;
GlobalArray : array[1..MaxSize] of Integer;
function getNextNumber : Integer;
begin
Result := NextNumber;
Inc(NextNumber);
end;
procedure TFooThread.Execute;
var
I : Integer;
begin
OnTerminate := Form1.ThreadsDone;
for I := 1 to MaxSize do
begin
GlobalArray[I] := getNextNumber;
Sleep(5);
end;
end;
procedure TForm1.ThreadsDone(Sender: TObject);
var
I : Integer;
begin
Inc(DoneFlag);
if DoneFlag = 2 then
for I := 1 to MaxSize do
ListBox1.Items.Add(IntToStr(GlobalArray[I]));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TFooThread.Create(false);
TFooThread.Create(false);
end;
end.
然而上面的代码却没有很好的显示出结果。问题出在2根线程同时抢占了数组。
解决的办法是让2个线程同步访问全局数组。
方案如下:
1.使用临界区。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure ThreadsDone(Sender: TObject);
public
{ Public declarations }
end;
TFooThread = class(TThread)
protected
procedure Execute; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
MaxSize = 128;
var
NextNumber : Integer = 0;
DoneFlag : Integer = 0;
GlobalArray : array[1..MaxSize] of Integer;
CS : TRTLCriticalSection;
function getNextNumber : Integer;
begin
Result := NextNumber;
Inc(NextNumber);
end;
procedure TFooThread.Execute;
var
I : Integer;
begin
OnTerminate := Form1.ThreadsDone;
EnterCriticalSection(CS);
for I := 1 to MaxSize do
begin
GlobalArray[I] := getNextNumber;
Sleep(5);
end;
LeaveCriticalSection(CS);
end;
procedure TForm1.ThreadsDone(Sender: TObject);
var
I : Integer;
begin
Inc(DoneFlag);
if DoneFlag = 2 then
begin
for I := 1 to MaxSize do
ListBox1.Items.Add(IntToStr(GlobalArray[I]));
DeleteCriticalSection(CS);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
InitializeCriticalSection(CS);
TFooThread.Create(false);
TFooThread.Create(false);
end;
end.
2.使用互斥量
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure ThreadsDone(Sender: TObject);
public
{ Public declarations }
end;
TFooThread = class(TThread)
protected
procedure Execute; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
MaxSize = 128;
var
NextNumber : Integer = 0;
DoneFlag : Integer = 0;
GlobalArray : array[1..MaxSize] of Integer;
hMutex : THandle = 0;
function getNextNumber : Integer;
begin
Result := NextNumber;
Inc(NextNumber);
end;
procedure TFooThread.Execute;
var
I : Integer;
begin
// FreeOnTerminate := True;
OnTerminate := Form1.ThreadsDone;
if WaitForSingleObject(hMutex, INFINITE) = WAIT_OBJECT_0 then
begin
for I := 1 to MaxSize do
begin
GlobalArray[I] := getNextNumber;
Sleep(5);
end;
end;
ReleaseMutex(hMutex);
end;
procedure TForm1.ThreadsDone(Sender: TObject);
var
I : Integer;
begin
Inc(DoneFlag);
if DoneFlag = 2 then
begin
for I := 1 to MaxSize do
ListBox1.Items.Add(IntToStr(GlobalArray[I]));
CloseHandle(hMutex);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
hMutex := CreateMutex(nil, False, nil);
TFooThread.Create(false);
TFooThread.Create(false);
end;
end.
3.使用信号量
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure ThreadsDone(Sender: TObject);
public
{ Public declarations }
end;
TFooThread = class(TThread)
protected
procedure Execute; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
MaxSize = 128;
var
NextNumber : Integer = 0;
DoneFlag : Integer = 0;
GlobalArray : array[1..MaxSize] of Integer;
hSem : THandle = 0;
function getNextNumber : Integer;
begin
Result := NextNumber;
Inc(NextNumber);
end;
procedure TFooThread.Execute;
var
I : Integer;
begin
OnTerminate := Form1.ThreadsDone;
if WaitForSingleObject(hSem, INFINITE) = WAIT_OBJECT_0 then
begin
for I := 1 to MaxSize do
begin
GlobalArray[I] := getNextNumber;
Sleep(5);
end;
end;
ReleaseSemaphore(hSem, 1, nil);
end;
procedure TForm1.ThreadsDone(Sender: TObject);
var
I : Integer;
begin
Inc(DoneFlag);
if DoneFlag = 2 then
begin
for I := 1 to MaxSize do
ListBox1.Items.Add(IntToStr(GlobalArray[I]));
CloseHandle(hSem);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
hSem := CreateSemaphore(nil, 1, 1, nil);
TFooThread.Create(false);
TFooThread.Create(false);
end;
end.