原理分析:
互斥對象是系統核心對象, 各線程都可以擁有它, 誰擁有誰就能執行;
執行完畢, 用 ReleaseMutex 函數釋放擁有權, 以讓其他等待的線程使用.
其他線程可用 WaitForSingleObject 函數排隊等候(等候也可以理解為排隊申請).
使用過程:
var hMutex: THandle; {應該先聲明一個全域的互斥控制代碼}CreateMutex {建立一個互斥對象}WaitForSingleObject {用等待函數排隊等候}ReleaseMutex {釋放擁有權}CloseHandle {最後釋放互斥對象}ReleaseMutex、CloseHandle 的參數都是 CreateMutex 返回的控制代碼, 關鍵是 CreateMutex 函數:
function CreateMutex( lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; {是否讓建立者(此例中是主線程)擁有該互斥對象} lpName: PWideChar {可以給此互斥對象取個名字, 如果不要名字可賦值為 nil}): THandle;{1、第一個參數前面說過.2、第二個參數在這裡一定要是 False, 如果讓主線程擁有互斥, 從理論上講, 得等程式退出後其他線程才有機會; 取值 False 時, 第一個執行的線程將會最先擁有互斥對象, 一旦擁有其他線程就得先等等.3、第三個參數, 如果給個名字, 函數將從系統中尋找是否有重名的互斥對象, 如果有則返回同名對象的存在的控制代碼; 如果賦值為 nil 將直接建立一個新的互斥對象; 下個例子將會有名字. }var f: Integer; {用這個變數協調一下各線程輸出的位置} hMutex: THandle; {互斥對象的控制代碼}function MyThreadFun(p: Pointer): DWORD; stdcall;var i,y: Integer;begin Inc(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.Button1Click(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, 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;Mutex 作為系統核心對象是可以跨進程的(臨界區就不行), 我們可以利用互斥對象禁止程式重複啟動.
工作思路:
先用 OpenMutex 嘗試開啟一個自訂名稱的 Mutex 對象, 如果開啟失敗說明之前沒有這個對象存在;
如果之前沒有這個對象, 馬上用 CreateMutex 建立一個, 此時的程式應該是第一次啟動;
再重複啟動時, 那個 OpenMutex 就有結果了, 然後強制退出.
最後在程式結束時用 CloseHandle 釋放 Mutex 對象.
function OpenMutex( dwDesiredAccess: DWORD; {開啟許可權} bInheritHandle: BOOL; {能否被當前程式建立的進程繼承} pName: PWideChar {Mutex 對象的名稱}): THandle; stdcall; {成功返回 Mutex 的控制代碼; 失敗返回 0}注意, 這裡的 CreateMutex 函數應該有個名了, 因為 OpenMutex 要用到;
另外, CreateMutex 的第二個參數已經不重要了(也就是 True 和 False 都行), 因為這裡是用其名稱來判斷的.
程式可以這樣寫:
unit 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 *.dfm}var hMutex: THandle;const NameMutex = 'MyMutex';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.這一般都是寫在 dpr 主程式裡, 省得讓後啟動的程式執行些無用的代碼:
program Project1;uses Forms, Windows, Unit1 in 'Unit1.pas' {Form1};{$R *.res}var 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.