1、Delphi多线程编程1011多线程同步之 Mutex 互斥对象原理分析:互斥对象是系统内核对象, 各线程都可以拥有它, 谁拥有谁就能执行; 执行完毕, 用 ReleaseMutex 函数释放拥有权, 以让其他等待的线程使用.其他线程可用 WaitForSingleObject 函数排队等候(等候也可以理解为排队申请).使用过程:var hMutex: THandle; 应该先声明一个全局的互斥句柄CreateMutex 建立一个互斥对象WaitForSingleObject 用等待函数排队等候ReleaseMutex 释放拥有权CloseHandle 最后释放互斥对象ReleaseMutex
2、、CloseHandle 的参数都是 CreateMutex 返回的句柄, 关键是 CreateMutex 函数:function CreateMutex( lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; 是否让创建者(此例中是主线程)拥有该互斥对象 lpName: PWideChar 可以给此互斥对象取个名字, 如果不要名字可赋值为 nil): THandle;1、第一个参数前面说过.2、第二个参数在这里一定要是 False, 如果让主线程拥有互斥, 从理论上讲, 得等程序退出后其他线程才有机会; 取值 False 时
3、, 第一个执行的线程将会最先拥有互斥对象, 一旦拥有其他线程就得先等等.3、第三个参数, 如果给个名字, 函数将从系统中寻找是否有重名的互斥对象, 如果有则返回同名对象的存在的句柄; 如果赋值为 nil 将直接创建一个新的互斥对象; 下个例子将会有名字. 本例效果图:代码文件: unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;type TForm1 = class(TForm) Button1: TButto
4、n; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); end;var Form1: TForm1;implementation$R *.dfmvar f: Integer; 用这个变量协调一下各线程输出的位置 hMutex: THandle; 互斥对象的句柄function MyThreadFun(p: Pointer): DWORD; stdcall;var i,y: Integer;begin Inc
5、(f); y := 20 * f; for i := 0 to 50000 do begin if WaitForSingleObject(hMutex, INFINITE) = WAIT_OBJECT_0 then begin Form1.Canvas.Lock; Form1.Canvas.TextOut(20, y, IntToStr(i); Form1.Canvas.Unlock; Sleep(0); 稍稍耽搁一点, 不然有时 Canvas 会协调不过来 ReleaseMutex(hMutex); end; end; Result := 0;end;procedure TForm1.Bu
6、tton1Click(Sender: TObject);var ThreadID: DWORD;begin Repaint; f := 0; CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID); CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID); CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID); CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID); CreateThread(nil,
7、 0, MyThreadFun, nil, 0, ThreadID);end;procedure TForm1.FormCreate(Sender: TObject);begin hMutex := CreateMutex(nil, False, nil);end;procedure TForm1.FormDestroy(Sender: TObject);begin CloseHandle(hMutex);end;end.窗体文件: object Form1: TForm1 Left = 0 Top = 0 Caption = Form1 ClientHeight = 140 ClientWi
8、dth = 192 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = Tahoma Font.Style = OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object Button1: TButton Left = 109 Top = 107 Width = 75 Height = 25 Caption = Button1
9、 TabOrder = 0 OnClick = Button1Click endendSyncObjs 单元下有封装好的 TMutex 类, 好像不如 Api 快, 内部机制也稍有区别, 但使用方法差不多: unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TO
10、bject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); end;var Form1: TForm1;implementation$R *.dfmuses SyncObjs;var f: Integer; MyMutex: TMutex;function MyThreadFun(p: Pointer): DWORD; stdcall;var i,y: Integer;begin Inc(f); y := 20 * f; for i := 0 to 50000 do begin i
11、f MyMutex.WaitFor(INFINITE) = wrSignaled then begin Form1.Canvas.Lock; Form1.Canvas.TextOut(20, y, IntToStr(i); Form1.Canvas.Unlock; MyMutex.Release; end; end; Result := 0;end;procedure TForm1.Button1Click(Sender: TObject);var ThreadID: DWORD;begin Repaint; f := 0; CreateThread(nil, 0, MyThreadFun,
12、nil, 0, ThreadID); CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID); CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID); CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID); CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);end;procedure TForm1.FormCreate(Sender: TObject);begin MyMutex := TMut
13、ex.Create(False);end;procedure TForm1.FormDestroy(Sender: TObject);begin MyMutex.Free;end;end.Mutex 作为系统核心对象是可以跨进程的(临界区就不行), 我们可以利用互斥对象禁止程序重复启动.工作思路:先用 OpenMutex 尝试打开一个自定义名称的 Mutex 对象, 如果打开失败说明之前没有这个对象存在;如果之前没有这个对象, 马上用 CreateMutex 建立一个, 此时的程序应该是第一次启动;再重复启动时, 那个 OpenMutex 就有结果了, 然后强制退出.最后在程序结束时用 Clo
14、seHandle 释放 Mutex 对象.function OpenMutex( dwDesiredAccess: DWORD; 打开权限 bInheritHandle: BOOL; 能否被当前程序创建的进程继承 pName: PWideChar Mutex 对象的名称): THandle; stdcall; 成功返回 Mutex 的句柄; 失败返回 0注意, 这里的 CreateMutex 函数应该有个名了, 因为 OpenMutex 要用到;另外, CreateMutex 的第二个参数已经不重要了(也就是 True 和 False 都行), 因为这里是用其名称来判断的.程序可以这样写: u
15、nit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs;type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); end;var Form1: TForm1;implementation$R *.dfmvar hMutex: THandle;const NameMutex = MyMut
16、ex;procedure TForm1.FormCreate(Sender: TObject);begin if OpenMutex(MUTEX_ALL_ACCESS, False, NameMutex) 0 then begin ShowMessage(该程序已启动); Application.Terminate; end; hMutex := CreateMutex(nil, False, NameMutex);end;procedure TForm1.FormDestroy(Sender: TObject);begin CloseHandle(hMutex);end;end.这一般都是写
17、在 dpr 主程序里, 省得让后启动的程序执行些无用的代码: program Project1;uses Forms, Windows, Unit1 in Unit1.pas Form1;$R *.resvar hMutex: THandle;const NameMutex = MyMutex;begin 主线程入口 if OpenMutex(MUTEX_ALL_ACCESS, False, NameMutex) 0 then begin MessageBox(0, 该程序已启动, 提示, MB_OK); Application.Terminate; end; hMutex := CreateMutex(nil, False, NameMutex); Application.Initialize; Application.MainFormOnTaskbar := True; Application.CreateForm(TForm1, Form1); Application.Run; CloseHandle(hMutex); 主线程出口end.
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1