通过事件同步主线程与从线程的实例
▲ 实例定义了一个主类: Tlistfile。该类主要是根具recmail变量,把它存入一个文件或从一个文件中读取出来。在该类上可实现:两个或多个应用程序之间通过文件进行异步问答式通讯。所谓问答式异步通讯是:一个程序发问,只有等到其他程序回答后才能继续发问。在一个时刻只能有一个程序使用该通讯文件,并且发送信息时必须进快完成。
▲该类约定了存取所用的文件是同一个文件且存在。
▲存recmail变量用savereclist方法,读recmail用loadreclist方法。
●savereclist方法实现:用TsaveThread线程实现。
当外界调用savereclist方法时,该方法首先创建一个TsaveThread线程,然后运行它,由它完成存取文件的操作,接着挂起主线程,等等TsaveThread线程执行完毕,TsaveThread线程执行完毕唤醒主线程。为了防止多个TsaveThread线程之间的干扰,在TsaveThread线程内用了临界区同步变量进行线程同步。
●loadreclist方法实现:用TloadThread线程实现。
当外界调用loadreclist方法时,该方法首先创建一个TloadThread线程,然后运行它,由它完成读取文件的操作,接着挂起主线程,等等TloadThread线程执行完毕,TloadThread线程执行完毕唤醒主线程。为了防止多个TloadThread线程之间的干扰,在TloadThread线程内用了临界区同步变量进行线程同步。
unit listfile;
interface
uses Classes,SysUtils,StrUtils,ExtCtrls,SyncObjs,Dialogs,windows;
type
precmail =^trecmail;
Trecmail =record
subject :string[150];//异步通信内容
recDate :tdatetime;//通信时时间
askBL,answerSBL,answerFBL :boolean;//定义了文件的问、开始回答、回答完毕标识。
end;
TlistFile=class //主类
public
recmail:Trecmail;
constructor create(filename:string);//创建时获得通讯文件名
procedure savereclist();
procedure loadreclist();
destructor destroy;
end;
TLoadThread=class(TThread) //读取线程
protected
Procedure Execute;override;
end;
TSaveThread=class(TThread)式//存取线程
protected
Procedure Execute;override;
end;
function IsFileInUse(fName : string ) : boolean;//文件在用检测函数
var
srecmail:Trecmail;//读存线程与主线程的recmail通信所用的变量
sfilename:string;// 异步通讯文件变量
Critical1,Critical2:TCriticalSection;//分别是读线程、存线程同步所用的临界变量
sevent1,sevent2:tsimpleEvent; /分别是读线程与主线程、存线程与主线程同步所用的同步事件变量
implementation
function IsFileInUse(fName : string ) : boolean;
var
HFileRes : Tfilestream;
begin
Result := false;
if not FileExists(fName) then
exit;
try
hfileres:=Tfilestream.create(fname,fmShareExclusive);
result:=false;
except
result:=true;
end;
try
hfileres.Free;
except
end;
end;
procedure TLoadThread.Execute;//读线程的执行代在码
var
fl:file of trecmail; loadBL:boolean;
begin
Critical1.Enter ;//临界区同步开始
loadBL:=true;
while true do //循环的原因是当通信文件还在使用是就不停的等待,直到可以使用该文件
begin
if not (IsFileInUse(sfilename)) then
begin
if not loadBL then
begin
Critical1.Leave ;
sevent1.SetEvent ;//唤醒挂起的主线程
exit;
end else
begin
AssignFile(fl,sfilename);
reset(fl);
read(fl,srecmail);
closefile(fl);
loadBL:=false;
end;
end;
end;
Critical1.Leave ; //临界区同步结束
end;
procedure TSaveThread.Execute;// 存线程的执行代在码
var
fl:file of trecmail; saveBL:boolean;
begin
Critical2.Enter ; //临界区同步开始
saveBL:=TRUE;
while true do //循环的原因是当通信文件还在使用是就不停的等待,直到可以使用该文件
begin
if (not IsFileInUse(sfilename)) then
begin
if not saveBL then
begin
Critical2.Leave ;
sevent2.SetEvent ; //唤醒挂起的主线程
exit;
end else
begin
AssignFile(fl,sfilename);
rewrite(fl);
write(fl,srecmail);
closefile(fl);
saveBL:=false;
end;
end;
end;
Critical2.Leave ; //临界区同步结束
end;
constructor TlistFile.create(filename:string);
begin
sfilename:= filename;//得到同步文件名
Critical1:= TCriticalSection.Create ;//创建读临界区同步对象
Critical2:= TCriticalSection.Create ; //创建存临界区同步对象
end;
procedure TlistFile.savereclist();//存文件操作
var
saveThread:TSaveThread;
begin
srecmail:=recmail ;//得存文件时,存线程要用到的要存通讯数据
sevent2:=TSimpleEvent.Create ;//创建与存线程同步所用的同步事件对象
sevent2.ResetEvent;//此处决定了该事件操纵的线程是主线程,因为第一次调用它的是主线程。
//用了该方法后,再用WaitFor(INFINITE);时,主线程将被挂起
saveThread:= TSaveThread.create(true);//创建存线程对象,创建后不立即执行。
saveThread.FreeOnTerminate :=true;//此处是当存线程执行完毕后自动灭亡,并释放资源。
saveThread.Resume;//唤醒存线程让之运行,以执行存文件任务
sevent2.WaitFor(INFINITE);//主线程被挂起,直到存线程执行完毕唤醒它
sevent1.Release ;//释放sevent2所用到的资源
end;
procedure TlistFile.loadreclist();//读文件操作
var
LoadThread:TLoadThread;
begin
sevent1:=TSimpleEvent.Create ;// 创建与读线程同步所用的同步事件对象
sevent1.ResetEvent; //此处决定了该事件操纵的线程是主线程,因为第一次调用它的是主线程。
//用了该方法后,再用WaitFor(INFINITE);时,主线程将被挂起
LoadThread:= TLoadThread.create(true); //创建读线程对象,创建后不立即执行。
LoadThread.FreeOnTerminate :=true;
loadThread.Resume ; //唤醒读线程让之运行,以执行读文件任务
sevent1.WaitFor(INFINITE); //此处是当读线程执行完毕后自动灭亡,并释放资源。
recmail:=srecmail ; //主线程得到读线程读到的通讯数据,以使文件通过recmail访问
sevent1.Release ;//释放sevent1所用到的资源
end;
destructor TlistFile.destroy;
begin
inherited destroy;
Critical1.Destroy ;
Critical2.Destroy ;
end;
end.
▲ 实例定义了一个主类: Tlistfile。该类主要是根具recmail变量,把它存入一个文件或从一个文件中读取出来。在该类上可实现:两个或多个应用程序之间通过文件进行异步问答式通讯。所谓问答式异步通讯是:一个程序发问,只有等到其他程序回答后才能继续发问。在一个时刻只能有一个程序使用该通讯文件,并且发送信息时必须进快完成。
▲该类约定了存取所用的文件是同一个文件且存在。
▲存recmail变量用savereclist方法,读recmail用loadreclist方法。
●savereclist方法实现:用TsaveThread线程实现。
当外界调用savereclist方法时,该方法首先创建一个TsaveThread线程,然后运行它,由它完成存取文件的操作,接着挂起主线程,等等TsaveThread线程执行完毕,TsaveThread线程执行完毕唤醒主线程。为了防止多个TsaveThread线程之间的干扰,在TsaveThread线程内用了临界区同步变量进行线程同步。
●loadreclist方法实现:用TloadThread线程实现。
当外界调用loadreclist方法时,该方法首先创建一个TloadThread线程,然后运行它,由它完成读取文件的操作,接着挂起主线程,等等TloadThread线程执行完毕,TloadThread线程执行完毕唤醒主线程。为了防止多个TloadThread线程之间的干扰,在TloadThread线程内用了临界区同步变量进行线程同步。
unit listfile;
interface
uses Classes,SysUtils,StrUtils,ExtCtrls,SyncObjs,Dialogs,windows;
type
precmail =^trecmail;
Trecmail =record
subject :string[150];//异步通信内容
recDate :tdatetime;//通信时时间
askBL,answerSBL,answerFBL :boolean;//定义了文件的问、开始回答、回答完毕标识。
end;
TlistFile=class //主类
public
recmail:Trecmail;
constructor create(filename:string);//创建时获得通讯文件名
procedure savereclist();
procedure loadreclist();
destructor destroy;
end;
TLoadThread=class(TThread) //读取线程
protected
Procedure Execute;override;
end;
TSaveThread=class(TThread)式//存取线程
protected
Procedure Execute;override;
end;
function IsFileInUse(fName : string ) : boolean;//文件在用检测函数
var
srecmail:Trecmail;//读存线程与主线程的recmail通信所用的变量
sfilename:string;// 异步通讯文件变量
Critical1,Critical2:TCriticalSection;//分别是读线程、存线程同步所用的临界变量
sevent1,sevent2:tsimpleEvent; /分别是读线程与主线程、存线程与主线程同步所用的同步事件变量
implementation
function IsFileInUse(fName : string ) : boolean;
var
HFileRes : Tfilestream;
begin
Result := false;
if not FileExists(fName) then
exit;
try
hfileres:=Tfilestream.create(fname,fmShareExclusive);
result:=false;
except
result:=true;
end;
try
hfileres.Free;
except
end;
end;
procedure TLoadThread.Execute;//读线程的执行代在码
var
fl:file of trecmail; loadBL:boolean;
begin
Critical1.Enter ;//临界区同步开始
loadBL:=true;
while true do //循环的原因是当通信文件还在使用是就不停的等待,直到可以使用该文件
begin
if not (IsFileInUse(sfilename)) then
begin
if not loadBL then
begin
Critical1.Leave ;
sevent1.SetEvent ;//唤醒挂起的主线程
exit;
end else
begin
AssignFile(fl,sfilename);
reset(fl);
read(fl,srecmail);
closefile(fl);
loadBL:=false;
end;
end;
end;
Critical1.Leave ; //临界区同步结束
end;
procedure TSaveThread.Execute;// 存线程的执行代在码
var
fl:file of trecmail; saveBL:boolean;
begin
Critical2.Enter ; //临界区同步开始
saveBL:=TRUE;
while true do //循环的原因是当通信文件还在使用是就不停的等待,直到可以使用该文件
begin
if (not IsFileInUse(sfilename)) then
begin
if not saveBL then
begin
Critical2.Leave ;
sevent2.SetEvent ; //唤醒挂起的主线程
exit;
end else
begin
AssignFile(fl,sfilename);
rewrite(fl);
write(fl,srecmail);
closefile(fl);
saveBL:=false;
end;
end;
end;
Critical2.Leave ; //临界区同步结束
end;
constructor TlistFile.create(filename:string);
begin
sfilename:= filename;//得到同步文件名
Critical1:= TCriticalSection.Create ;//创建读临界区同步对象
Critical2:= TCriticalSection.Create ; //创建存临界区同步对象
end;
procedure TlistFile.savereclist();//存文件操作
var
saveThread:TSaveThread;
begin
srecmail:=recmail ;//得存文件时,存线程要用到的要存通讯数据
sevent2:=TSimpleEvent.Create ;//创建与存线程同步所用的同步事件对象
sevent2.ResetEvent;//此处决定了该事件操纵的线程是主线程,因为第一次调用它的是主线程。
//用了该方法后,再用WaitFor(INFINITE);时,主线程将被挂起
saveThread:= TSaveThread.create(true);//创建存线程对象,创建后不立即执行。
saveThread.FreeOnTerminate :=true;//此处是当存线程执行完毕后自动灭亡,并释放资源。
saveThread.Resume;//唤醒存线程让之运行,以执行存文件任务
sevent2.WaitFor(INFINITE);//主线程被挂起,直到存线程执行完毕唤醒它
sevent1.Release ;//释放sevent2所用到的资源
end;
procedure TlistFile.loadreclist();//读文件操作
var
LoadThread:TLoadThread;
begin
sevent1:=TSimpleEvent.Create ;// 创建与读线程同步所用的同步事件对象
sevent1.ResetEvent; //此处决定了该事件操纵的线程是主线程,因为第一次调用它的是主线程。
//用了该方法后,再用WaitFor(INFINITE);时,主线程将被挂起
LoadThread:= TLoadThread.create(true); //创建读线程对象,创建后不立即执行。
LoadThread.FreeOnTerminate :=true;
loadThread.Resume ; //唤醒读线程让之运行,以执行读文件任务
sevent1.WaitFor(INFINITE); //此处是当读线程执行完毕后自动灭亡,并释放资源。
recmail:=srecmail ; //主线程得到读线程读到的通讯数据,以使文件通过recmail访问
sevent1.Release ;//释放sevent1所用到的资源
end;
destructor TlistFile.destroy;
begin
inherited destroy;
Critical1.Destroy ;
Critical2.Destroy ;
end;
end.