delphi 关于多线程同步的一些用法
线程是进程内一个相对独立的、 可调度的执行单元。 一个应用可以有一个主线程, 一个主线程可以有多个子线程,子线程还可以有自己的子线程,这样就构成了多 线程应用了。由于多个线程往往会同时访问同一块内存区域,频繁的访问这块区 域,将会增加产生线程冲突的概率。一旦产生了冲突,将会造成不可预料的结果 (比如:该公用区域的值是不可预料的),可见处理线程同步的必要性。 (注意: 本文中出现的所有代码都是用 DELPHI 描述的, 调试环境为 Windows me ,Delphi 6。其中所涉及的 Windows API 函数可以从 MSDN 获得详细的
文档。) 首先引用一个实例来引出我们以下的讨论,该实例没有采取任何措施来避 免线程冲突。 它的主要过程为: 由主线程启动两个线程对 letters 这个全局变量进行 频繁的读写,然后分别把修改的结果显示到 ListBox 中。由于没有同步这两个线 程,使得线程在修改 letters 时产生了不可预料的结果。 ListBox 中的每一行的字母都应该一致,但是上图画线处则不同,这就 是线程冲突产生的结果。当两个线程同时访问该共享内存时,一个线程还未对该 内存修改完, 另一个线程又对该内存进行了修改, 由于写值的过程没有被串行化, 这样就产生了无效的结果。可见线程同步的重要性。 以下是本例的代码 unit.pas 文件 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; //定义窗口类 type TForm1 = class(TForm) ListBox1: TListBox; ListBox2: TListBox; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; //定义线程类 type TListThread=class(TThread)
private Str:String; protected procedure AddToList;//将 Str 加入 ListBox 组件 Procedure Execute;override; public LBox:TListBox; //定义一个线程内公共变量,接收从进程传进来的 TListBox; end;
//定义变量 var Form1: TForm1; Letters:String='AAAAAAAAAAAAAAAAAAAA';//全局变量 implementation {$R *.dfm} //线程类实现部分 procedure TListThread.Execute; var I,J,K:Integer; begin for i:=0 to 50 do begin for J:=1 to 20 do for K:=1 to 1000 do//循环 1000 次增加产生冲突的几率 if letters[j]<'Z' then letters[j]:=succ(Letters[j]) else letters[j]:='A'; str:=letters; synchronize(addtolist);//同步访问 VCL 可视组件(本例中,这个 同步可以不要,见 Button1Click); end; end; procedure TListThread.AddToList; begin LBox.Items.Add(str);//将 str 加入
列表框 end;
//窗口类实现部分 procedure TForm1.Button1Click(Sender: TObject); var th1,th2:TListThread; begin Listbox1.Clear; Listbox2.Clear; th1:=tlistThread.Create(true);//创建线程 1 th2:=tlistThread.Create(true);//创建线程 2 th1.L
Box:=listBox1;//不同的 VCL 控件; th2.LBox:=listBox2;//不同的 VCL 控件; th1.Resume;//开始执行 th2.Resume; end; end. 由上例可见,当多个线程同时修改一个公用变量时,会产生冲突,所以我 们要设法防止它,这样我们开发的多线程应用才能够稳定地运行。下面我们来改 进它。我们先使用临界段来串行化,实现同步。 在上例 unit1.pas 代码的 uses 段中加入 SyncObjs 单元, 加入全局临界段变量:Critical1:TRTLCriticalSection 在 FormCreate 事件中加入:InitializeCriticalSection(Critical1)这句 代码, 在 FormDestroy 事件中加入 DeleteCriticalSection(Critical1)这句代码, 然后修改 TListThread.Execute 函数,修改后的代码似如下所示(?处为增 加的代码): procedure TListThread.Execute; var I,J,K:Integer; begin for i:=0 t