zoukankan      html  css  js  c++  java
  • 多线程编程(4) 多线程同步之 Mutex (互斥对象)

    原理分析:
    互斥对象是系统内核对象, 各线程都可以拥有它, 谁拥有谁就能执行;
    执行完毕, 用 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;
    
    interface
    
    uses
      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.
    
  • 相关阅读:
    Java实现 LeetCode 833 字符串中的查找与替换(暴力模拟)
    Java实现 LeetCode 833 字符串中的查找与替换(暴力模拟)
    Java实现 LeetCode 833 字符串中的查找与替换(暴力模拟)
    Java实现 LeetCode 832 翻转图像(位运算)
    Java实现 LeetCode 832 翻转图像(位运算)
    Java实现 LeetCode 832 翻转图像(位运算)
    Java实现 LeetCode 831 隐藏个人信息(暴力)
    Java实现 LeetCode 831 隐藏个人信息(暴力)
    Java实现 LeetCode 831 隐藏个人信息(暴力)
    how to use automapper in c#, from cf~
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/2940722.html
Copyright © 2011-2022 走看看