关于两个进程CreateMutex创建同名互斥体

ooolinux 2016-08-06 02:36:46
如果一个进程A(线程A):HANDLE hMutexA=CreateMutex(NULL,true,"MyAppMutex"); 创建了一个互斥体,
然后另一个进程B(线程B)也:HANDLE hMutexB=CreateMutex(NULL,true,"MyAppMutex"); 创建了一个同名互斥体,
1、这个时候hMutexB会得到已经存在的互斥体的句柄吗?
2、这个时候互斥体由线程A拥有还是线程B拥有?
3、假如由线程B拥有,线程A会挂起吗?
4、如果线程B此时ReleaseMutex(hMutexB),线程A会重新获取互斥体并转为就绪态吗?
5、如果线程B不ReleaseMutex(hMutexB),而是CloseHandle(hMutexB),线程A会处于什么运行状态?
问题有点多,谢谢!
...全文
771 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
ooolinux 2016-08-08
  • 打赏
  • 举报
回复
引用 10 楼 lm_whales 的回复:
MFC 创建内核对象,和检测代码。 都写在 WinApp 的派生类的构造函数 或者他的InitInstance(); 没创建成功,直接调用 exit退出 创建成功,如果已经存在,也可以直接退出 如果可能,你也可以把一些信息,传递给上一个实例,不过这和 Mutex关系不大了 创建成功,并且是第一个实例,就可以做事情了 抱歉,摘抄的帮助,没整理直接发过来了 GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership. 意思是说,这个内核对象 hMutexB 只是获得同一个内核对象的句柄。 但是线程并没有理所当然的获得内核对象的所有权。 依然需要依赖等待函数,才能拥有 Mutex 的所有权。 也就是说,同步还是需要倚靠 wait,release 很函数,而不是打开方式 不过这和 利用内核对象,确认 进程有无实例运行没有关系了 只要 GetLastError returns ERROR_ALREADY_EXISTS,就表明。这个内核对象,已经存在了。 如果别的程序,不使用同名, 同一类型内核对象的话,就可以确定是同一个程序的不同实例了。 这里的前提是bInitialOwner is ignored,如果不是被忽略的话, 而是第一次成功创建内核对象--- 返回值不是 ERROR_ALREADY_EXISTS ----- bInitialOwner可以决定,线程创建内核对象,立即就拥有它的所以权,其他线程, 必须等候该线程释放 Mutex 的所有权。 才可以拥有(等到)他的所有权。
现在我清楚了
引用 6 楼 lm_whales 的回复:
微软的实例

/*******
Using Named Objects
The following example illustrates the use of object names by creating and opening a named mutex.

First Process
The first process uses the CreateMutex function to create the mutex object. Note that this function succeeds even if there is an existing object with the same name.
***************/

#include <windows.h>
#include <stdio.h>
#include <conio.h>

// This process creates the mutex object.

void main()
{
    HANDLE hMutex; 

    hMutex = CreateMutex( 
        NULL,                        // default security descriptor
        FALSE,                       // mutex not owned
        TEXT("NameOfMutexObject"));  // object name,这是直接写名字的,名字还可以有Gloabal\和Loacal\前缀

    if (hMutex == NULL) 
        printf("CreateMutex error: %d\n", GetLastError() ); //这种时候,啥也不能做,只好推出了
    else 
        if ( GetLastError() == ERROR_ALREADY_EXISTS ) //这表明 hMutex已经创建了,也就是进程已经有个实例在运行了
            printf("CreateMutex opened an existing mutex\n"); // 你的程序,这这时候退出,就可以了,注意随手CloseHandle
        else printf("CreateMutex created a new mutex.\n");//否则,这是第一个实例,下面就可以开工干活了
//........... 开工干活,MFC可以写在WinAPP 的派生类的构造函数中
    // Keep this process around until the second process is run
    _getch();
}
/***************
Second Process
The second process uses the OpenMutex function to open a handle to the existing mutex. This function fails if a mutex object with the specified name does not exist. The access parameter requests full access to the mutex object, which is necessary for the handle to be used in any of the wait functions.


#include <windows.h>
#include <stdio.h>

// This process opens a handle to a mutex created by another process.
*****************/
void main()
{
    HANDLE hMutex; 

    hMutex = OpenMutex( 
        MUTEX_ALL_ACCESS,            // request full access
        FALSE,                       // handle not inheritable
        TEXT("NameOfMutexObject"));  // object name

    if (hMutex == NULL) 
        printf("OpenMutex error: %d\n", GetLastError() );
    else printf("OpenMutex successfully opened the mutex.\n");
}
随手CloseHandle确实是好习惯
lm_whales 2016-08-08
  • 打赏
  • 举报
回复
bInitialOwner 参数为TRUE表示创建即拥有 为FALSE 表示创建不拥有,而是需要依赖等待函数取得所有权。
lm_whales 2016-08-08
  • 打赏
  • 举报
回复
MFC 创建内核对象,和检测代码。 都写在 WinApp 的派生类的构造函数 或者他的InitInstance(); 没创建成功,直接调用 exit退出 创建成功,如果已经存在,也可以直接退出 如果可能,你也可以把一些信息,传递给上一个实例,不过这和 Mutex关系不大了 创建成功,并且是第一个实例,就可以做事情了 抱歉,摘抄的帮助,没整理直接发过来了 GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership. 意思是说,这个内核对象 hMutexB 只是获得同一个内核对象的句柄。 但是线程并没有理所当然的获得内核对象的所有权。 依然需要依赖等待函数,才能拥有 Mutex 的所有权。 也就是说,同步还是需要倚靠 wait,release 很函数,而不是打开方式 不过这和 利用内核对象,确认 进程有无实例运行没有关系了 只要 GetLastError returns ERROR_ALREADY_EXISTS,就表明。这个内核对象,已经存在了。 如果别的程序,不使用同名, 同一类型内核对象的话,就可以确定是同一个程序的不同实例了。 这里的前提是bInitialOwner is ignored,如果不是被忽略的话, 而是第一次成功创建内核对象--- 返回值不是 ERROR_ALREADY_EXISTS ----- bInitialOwner可以决定,线程创建内核对象,立即就拥有它的所以权,其他线程, 必须等候该线程释放 Mutex 的所有权。 才可以拥有(等到)他的所有权。
lm_whales 2016-08-07
  • 打赏
  • 举报
回复
Using Named Objects The following example illustrates the use of object names by creating and opening a named mutex. First Process The first process uses the CreateMutex function to create the mutex object. Note that this function succeeds even if there is an existing object with the same name. #include <windows.h> #include <stdio.h> #include <conio.h> // This process creates the mutex object. void main() { HANDLE hMutex; hMutex = CreateMutex( NULL, // default security descriptor FALSE, // mutex not owned TEXT("NameOfMutexObject")); // object name if (hMutex == NULL) printf("CreateMutex error: %d\n", GetLastError() ); else if ( GetLastError() == ERROR_ALREADY_EXISTS ) printf("CreateMutex opened an existing mutex\n"); else printf("CreateMutex created a new mutex.\n"); // Keep this process around until the second process is run _getch(); } Second Process The second process uses the OpenMutex function to open a handle to the existing mutex. This function fails if a mutex object with the specified name does not exist. The access parameter requests full access to the mutex object, which is necessary for the handle to be used in any of the wait functions. #include <windows.h> #include <stdio.h> // This process opens a handle to a mutex created by another process. void main() { HANDLE hMutex; hMutex = OpenMutex( MUTEX_ALL_ACCESS, // request full access FALSE, // handle not inheritable TEXT("NameOfMutexObject")); // object name if (hMutex == NULL) printf("OpenMutex error: %d\n", GetLastError() ); else printf("OpenMutex successfully opened the mutex.\n"); }
lm_whales 2016-08-07
  • 打赏
  • 举报
回复
CreateMutex Function Creates or opens a named or unnamed mutex object. To specify an access mask for the object, use the CreateMutexEx function. HANDLE WINAPI CreateMutex( __in LPSECURITY_ATTRIBUTES lpMutexAttributes, __in BOOL bInitialOwner, __in LPCTSTR lpName ); Parameters lpMutexAttributes A pointer to a SECURITY_ATTRIBUTES structure. If this parameter is NULL, the handle cannot be inherited by child processes. The lpSecurityDescriptor member of the structure specifies a security descriptor for the new mutex. If lpMutexAttributes is NULL, the mutex gets a default security descriptor. The ACLs in the default security descriptor for a mutex come from the primary or impersonation token of the creator. For more information, see Synchronization Object Security and Access Rights. bInitialOwner If this value is TRUE and the caller created the mutex, the calling thread obtains initial ownership of the mutex object. Otherwise, the calling thread does not obtain ownership of the mutex. To determine if the caller created the mutex, see the Return Values section. lpName The name of the mutex object. The name is limited to MAX_PATH characters. Name comparison is case sensitive. If lpName matches the name of an existing named mutex object, this function requests the MUTEX_ALL_ACCESS access right. In this case, the bInitialOwner parameter is ignored because it has already been set by the creating process. If the lpMutexAttributes parameter is not NULL, it determines whether the handle can be inherited, but its security-descriptor member is ignored. If lpName is NULL, the mutex object is created without a name. If lpName matches the name of an existing event, semaphore, waitable timer, job, or file-mapping object, the function fails and the GetLastError function returns ERROR_INVALID_HANDLE. This occurs because these objects share the same name space. The name can have a "Global\" or "Local\" prefix to explicitly create the object in the global or session name space. The remainder of the name can contain any character except the backslash character (\). For more information, see Kernel Object Namespaces. Fast user switching is implemented using Terminal Services sessions. The first user to log on uses session 0, the next user to log on uses session 1, and so on. Kernel object names must follow the guidelines outlined for Terminal Services so that applications can support multiple users. Windows 2000: If Terminal Services is not running, the "Global\" and "Local\" prefixes are ignored. The remainder of the name can contain any character except the backslash character. The object can be created in a private namespace. For more information, see Object Namespaces. Return Value If the function succeeds, the return value is a handle to the newly created mutex object. If the function fails, the return value is NULL. To get extended error information, call GetLastError. If the mutex is a named mutex and the object existed before this function call, the return value is a handle to the existing object, GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership. However, if the caller has limited access rights, the function will fail with ERROR_ACCESS_DENIED and the caller should use the OpenMutex function.
ooolinux 2016-08-07
  • 打赏
  • 举报
回复
引用 1 楼 anhuizhuanjiao 的回复:
写出来试试不就知道了
引用 2 楼 lm_whales 的回复:
命名内核对象(同一用户)只会创建一份。 似乎是这样的 命名内核对象 是全局的,不允许重名 非命名 内核对象,是进程局部的 进程间通讯,可以用命名内核对象。
事实上,是一个程序要防止二次启动,不知道代码怎么写比较正确,因为对几个函数不太理解。
lm_whales 2016-08-07
  • 打赏
  • 举报
回复
命名内核对象(同一用户)只会创建一份。 似乎是这样的 命名内核对象 是全局的,不允许重名 非命名 内核对象,是进程局部的 进程间通讯,可以用命名内核对象。
ooolinux 2016-08-07
  • 打赏
  • 举报
回复
引用 8 楼 pengzhixi 的回复:
If the mutex is a named mutex and the object existed before this function call, the return value is a handle to the existing object, GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership. However, if the caller has limited access rights, the function will fail with ERROR_ACCESS_DENIED and the caller should use the OpenMutex function.
也就是说hMutexB得到已经存在的互斥体对象的句柄,对象使用计数加一? and the calling thread is not granted ownership 但是线程B不会拥有互斥体?
pengzhixi 2016-08-07
  • 打赏
  • 举报
回复
If the mutex is a named mutex and the object existed before this function call, the return value is a handle to the existing object, GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership. However, if the caller has limited access rights, the function will fail with ERROR_ACCESS_DENIED and the caller should use the OpenMutex function.
lm_whales 2016-08-07
  • 打赏
  • 举报
回复
微软的实例

/*******
Using Named Objects
The following example illustrates the use of object names by creating and opening a named mutex.

First Process
The first process uses the CreateMutex function to create the mutex object. Note that this function succeeds even if there is an existing object with the same name.
***************/

#include <windows.h>
#include <stdio.h>
#include <conio.h>

// This process creates the mutex object.

void main()
{
    HANDLE hMutex; 

    hMutex = CreateMutex( 
        NULL,                        // default security descriptor
        FALSE,                       // mutex not owned
        TEXT("NameOfMutexObject"));  // object name,这是直接写名字的,名字还可以有Gloabal\和Loacal\前缀

    if (hMutex == NULL) 
        printf("CreateMutex error: %d\n", GetLastError() ); //这种时候,啥也不能做,只好推出了
    else 
        if ( GetLastError() == ERROR_ALREADY_EXISTS ) //这表明 hMutex已经创建了,也就是进程已经有个实例在运行了
            printf("CreateMutex opened an existing mutex\n"); // 你的程序,这这时候退出,就可以了,注意随手CloseHandle
        else printf("CreateMutex created a new mutex.\n");//否则,这是第一个实例,下面就可以开工干活了
//........... 开工干活,MFC可以写在WinAPP 的派生类的构造函数中
    // Keep this process around until the second process is run
    _getch();
}
/***************
Second Process
The second process uses the OpenMutex function to open a handle to the existing mutex. This function fails if a mutex object with the specified name does not exist. The access parameter requests full access to the mutex object, which is necessary for the handle to be used in any of the wait functions.


#include <windows.h>
#include <stdio.h>

// This process opens a handle to a mutex created by another process.
*****************/
void main()
{
    HANDLE hMutex; 

    hMutex = OpenMutex( 
        MUTEX_ALL_ACCESS,            // request full access
        FALSE,                       // handle not inheritable
        TEXT("NameOfMutexObject"));  // object name

    if (hMutex == NULL) 
        printf("OpenMutex error: %d\n", GetLastError() );
    else printf("OpenMutex successfully opened the mutex.\n");
}
转角天边 2016-08-06
  • 打赏
  • 举报
回复
写出来试试不就知道了
一、临界区 所谓临界区,就是一次只能由一个线程来执行的一段代码。如果把初始化数组的代码放在临界区内,另一个线程在第一个线程处理完之前是不会被执行的。 使用临界区的步骤: 1、先声明一个全局变量类型为TRTLCriticalSection; 2、在线程Create()前调用InitializeCriticalSection()过程来初始化,该函数定义是: void WINAPI InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 类型lpCriticalSection即是Delphi封装的TRTLCriticalSection。 3、在线程的需要放入临界区的代码前面使用EnterCriticalSection(lpCriticalSection)过程来开始建立临界区。在代码完成后用LeaveCriticalSection(lpCriticalSection)来标志临界区的结束。 4、在线程执行完后用DeleteCriticalSection(lpCriticalSection)来清除临界区。这个清除过程必须放在线程执行完后的地方,比如FormDesroy事件中。上面的例子中,若把该过程放在TMyThread.Create(False);后,会产生错误。 二、互斥互斥非常类似于临界区,除了两个关键的区别:首先,互斥可用于跨进程的线程同步。其次,互斥能被赋予一个字符串名字,并且通过引用此名字创建现有互斥对象的附加句柄。 提示临界区与事件对象(比如互斥对象)的最大的区别是在性能上。临界区在没有线程冲突时,要用10~15个时间片,而事件对象由于涉及到系统内核要用400~600个时间片。 使用互斥的步骤: 1、声明一个类型为Thandle或Hwnd的全局变量,其实都是Cardinal类型。Hwnd是handle of window,主要用于窗口句柄;而Thandle则没有限制。 2、线程Create()前用CreateMutex()来创建一个互斥量。该函数定义为: HANDLE WINAPI CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName:Pchar); LPSECURITY_ATTRIBUTES参数为一个指向TSecurityAttributtes记录的指针。此参数设为nil,表示访问控制列表默认的安全属性。 bInitalOwner参数表示创建互斥对象的线程是否要成为此互斥对象的拥有者。当此参数为False时,表示互斥对象没有拥有者。 lpName参数指定互斥对象的名称。设为nil表示无命名,如果参数不是设为nil,函数会搜索是否有同名互斥对象存在。如果有,函数就会返回同名互斥对象的句柄。否则,就新创建一个互斥对象并返回其句柄。 返回值是一handle。当错误发生时,返回null,此时用GetLastError函数可查看错误的信息。 利用CreateMutex()可以防止程序多个实例运行,如下例: Program ABC; Uses Forms,Windows,…; {$R *.res} Var hMutex:Hwnd; Begin Application.Initialize; hMutex:=CreateMutex(nil,False,Pchar(Application.Title)); if GetLastErrorERROR_ALREADY_EXISTS then begin //项目要运行的咚咚 end; ReleaseMutex(hMutex); Application.Run; End; 在本节的例程中,我们只是要防止线程进入同步代码区域中,所以lpName参数设置为nil。 3、在同步代码前用WaitForSingleObject()函数。该函数使得线程取得互斥对象(同步代码)的拥有权。该函数定义为: DWORD WINAPI WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds); 这个函数可以使当前线程在dwMilliseconds指定的时间内睡眠,直到hHandle参数指定的对象进入发信号状态为止。一个互斥对象不再被线程拥有时,它就进入发信号状态。当一个进程要终止时,它就进入发信号状态。dwMilliseconds参数可以设为0,这意味着只检查hHandle参数指定的对象是否处于发信号状态,而后立即返回。dwMilliseconds参数设为INFINITE,表示如果信号不出现将一直等下去。 这个函数的返回值含义: WAIT_ABANDONED 指定的对象是互斥对象,并且拥有这个互斥对象的线程在没有释放此对象之前就已终止。此时就称互斥对象被抛弃。这种情况下,这个互斥对象归当前线程所有,并把它设为非发信号状态 WAIT_OBJECT_0 指定的对象处于发信号状态 WAIT_TIMEOUT 等待的时间已过,对象仍然是非发信号状态 再次声明,当一个互斥对象不再被一个线程所拥有,它就处于发信号状态。此时首先调用WaitForSingleObject()函数的线程就成为该互斥对象的拥有者,此互斥对象设为不发信号状态。当线程调用ReleaseMutex()函数并传递一个互斥对象的句柄作为参数时,这种拥有关系就被解除,互斥对象重新进入发信号状态。 注意除WaitForSingleObject()函数外,你还可以使用WaitForMultipleObject()和MsgWaitForMultipleObject()函数,它们可以等待几个对象变为发信号状态。这两个函数的详细情况请看Win32 API联机文档。 4、在同步代码结束后,使用ReleaseMutex(THandle)函数来标志。该函数只是释放互斥对象和线程的拥有者关系,并不释放互斥对象的句柄。 5、调用CloseHandle(THandle)来关闭互斥对象。请注意例程中该函数的使用位置。 三、还有一种用信号量对象来管理线程同步的,它是在互斥的基础上建立的,但信号量增加了资源计数的功能,预定数目的线程允许同时进入要同步的代码。有点复杂,想不到在哪可以用,现在就不研究论了。 unit Tst_Thread3U; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private procedure ThreadsDone(Sender: TObject); end; TMyThread=class(TThread) protected procedure Execute;override; end; var Form1: TForm1; implementation {$R *.dfm} const MaxSize=128; var NextNumber:Integer=0; DoneFlags:Integer=0; GlobalArry:array[1..MaxSize] of Integer; Lock:byte; //1-不同步 2-临界区 3-互斥 CS:TRTLCriticalSection; //临界区 hMutex:THandle; //互斥 function GetNextNumber:Integer; begin Result:=NextNumber; inc(NextNumber); end; procedure TMyThread.Execute; var i:Integer; begin FreeOnTerminate:=True; //终止后自动free OnTerminate:=Form1.ThreadsDone; if Lock3 then //非互斥情况 begin if Lock=2 then EnterCriticalSection(CS); //建立临界区 for i := 1 to MaxSize do begin GlobalArry[i]:=GetNextNumber; Sleep(5); end; if Lock=2 then LeaveCriticalSection(CS);//离开临界区 end else //-------互斥 begin if WaitForSingleObject(hMutex,INFINITE)=WAIT_OBJECT_0 then begin for i := 1 to MaxSize do begin GlobalArry[i]:=GetNextNumber; Sleep(5); end; end; ReleaseMutex(hMutex); //释放 end; end; procedure TForm1.ThreadsDone(Sender: TObject); var i:Integer; begin Inc(DoneFlags); if DoneFlags=2 then begin for i := 1 to MaxSize do Memo1.Lines.Add(inttostr(GlobalArry[i])); if Lock=2 then DeleteCriticalSection(CS); //删除临界区 If Lock=3 then CloseHandle(hMutex); //关闭互斥 end; end; //非同步 procedure TForm1.Button1Click(Sender: TObject); begin Lock:=1; TMyThread.Create(False); TMyThread.Create(False); end; //临界区 procedure TForm1.Button2Click(Sender: TObject); begin Lock:=2; InitializeCriticalSection(CS); //初始化临界区 TMyThread.Create(False); TMyThread.Create(False); end; //互斥 procedure TForm1.Button3Click(Sender: TObject); begin Lock:=3; // 互斥 hMutex:=CreateMutex(0,False,nil); TMyThread.Create(False); TMyThread.Create(False); end; end.
您查询的关键词是:delphi 同步 数据 。如果打开速度慢,可以尝试快速版;如果想保存快照,可以添加到搜藏。 (百度和网页http://blog.csdn.net/mygodsos/archive/2008/10/19/3097921.aspx的作者无关,不对其内容负责。百度快照谨为网络故障时之索引,不代表被搜索网站的即时页面。) -------------------------------------------------------------------------------- 发呆茶馆 登录 注册 欢迎 退出 我的博客 配置 写文章 文章管理 博客首页 全站 当前博客 空间 博客 好友 相册 留言 用户操作 [发私信] [加为好友] mygodsos 订阅我的博客 mygodsos的公告 文章分类 Delphi Delphi学习--多线程 Delphi学习--自创的常用函数 期货大事记 生活感悟 投资理财 编程学习 万一的Delphi博客 存档 2009年05月(3) 2008年11月(13) 2008年10月(8) 2008年09月(3) ◆Delphi多线程编程之三 同步读写全局数据 ◆(乌龙哈里2008-10-12) 收藏 ◆Delphi多线程编程之三同步读写全局数据 ◆(乌龙哈里2008-10-12) (调试环境:Delphi2007+WinXPsp3 例程:Tst_Thread3.dpr) 开始研究最重要的多线程读写全局数据了,结合书上的例子,我修改成下面的情况: unit Tst_Thread3U; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private procedure ThreadsDone(Sender: TObject); end; TMyThread=class(TThread) protected procedure Execute;override; end; var Form1: TForm1; implementation {$R *.dfm} const MaxSize=128; var NextNumber:Integer=0; DoneFlags:Integer=0; GlobalArry:array[1..MaxSize] of Integer; Lock:byte; //1-不同步 2-临界区 3-互斥 CS:TRTLCriticalSection; //临界区 hMutex:THandle; //互斥 function GetNextNumber:Integer; begin Result:=NextNumber; inc(NextNumber); end; procedure TMyThread.Execute; var i:Integer; begin FreeOnTerminate:=True; //终止后自动free OnTerminate:=Form1.ThreadsDone; if Lock3 then //非互斥情况 begin if Lock=2 then EnterCriticalSection(CS); //建立临界区 for i := 1 to MaxSize do begin GlobalArry[i]:=GetNextNumber; Sleep(5); end; if Lock=2 then LeaveCriticalSection(CS);//离开临界区 end else //-------互斥 begin if WaitForSingleObject(hMutex,INFINITE)=WAIT_OBJECT_0 then begin for i := 1 to MaxSize do begin GlobalArry[i]:=GetNextNumber; Sleep(5); end; end; ReleaseMutex(hMutex); //释放 end; end; procedure TForm1.ThreadsDone(Sender: TObject); var i:Integer; begin Inc(DoneFlags); if DoneFlags=2 then begin for i := 1 to MaxSize do Memo1.Lines.Add(inttostr(GlobalArry[i])); if Lock=2 then DeleteCriticalSection(CS); //删除临界区 If Lock=3 then CloseHandle(hMutex); //关闭互斥 end; end; //非同步 procedure TForm1.Button1Click(Sender: TObject); begin Lock:=1; TMyThread.Create(False); TMyThread.Create(False); end; //临界区 procedure TForm1.Button2Click(Sender: TObject); begin Lock:=2; InitializeCriticalSection(CS); //初始化临界区 TMyThread.Create(False); TMyThread.Create(False); end; //互斥 procedure TForm1.Button3Click(Sender: TObject); begin Lock:=3; // 互斥 hMutex:=CreateMutex(0,False,nil); TMyThread.Create(False); TMyThread.Create(False); end; end. 没有临界区和互斥的帮助,两个线程都不断地在Memo1输出,而且数字是乱的。 一、临界区 所谓临界区,就是一次只能由一个线程来执行的一段代码。如果把初始化数组的代码放在临界区内,另一个线程在第一个线程处理完之前是不会被执行的。 使用临界区的步骤: 1、先声明一个全局变量类型为TRTLCriticalSection; 2、在线程Create()前调用InitializeCriticalSection()过程来初始化,该函数定义是: void WINAPI InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 类型lpCriticalSection即是Delphi封装的TRTLCriticalSection。 3、在线程的需要放入临界区的代码前面使用EnterCriticalSection(lpCriticalSection)过程来开始建立临界区。在代码完成后用LeaveCriticalSection(lpCriticalSection)来标志临界区的结束。 4、在线程执行完后用DeleteCriticalSection(lpCriticalSection)来清除临界区。这个清除过程必须放在线程执行完后的地方,比如FormDesroy事件中。上面的例子中,若把该过程放在TMyThread.Create(False);后,会产生错误。 二、互斥互斥非常类似于临界区,除了两个关键的区别:首先,互斥可用于跨进程的线程同步。其次,互斥能被赋予一个字符串名字,并且通过引用此名字创建现有互斥对象的附加句柄。 提示临界区与事件对象(比如互斥对象)的最大的区别是在性能上。临界区在没有线程冲突时,要用10~15个时间片,而事件对象由于涉及到系统内核要用400~600个时间片。 使用互斥的步骤: 1、声明一个类型为Thandle或Hwnd的全局变量,其实都是Cardinal类型。Hwnd是handle of window,主要用于窗口句柄;而Thandle则没有限制。 2、线程Create()前用CreateMutex()来创建一个互斥量。该函数定义为: HANDLE WINAPI CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName:Pchar); LPSECURITY_ATTRIBUTES参数为一个指向TSecurityAttributtes记录的指针。此参数设为nil,表示访问控制列表默认的安全属性。 bInitalOwner参数表示创建互斥对象的线程是否要成为此互斥对象的拥有者。当此参数为False时,表示互斥对象没有拥有者。 lpName参数指定互斥对象的名称。设为nil表示无命名,如果参数不是设为nil,函数会搜索是否有同名互斥对象存在。如果有,函数就会返回同名互斥对象的句柄。否则,就新创建一个互斥对象并返回其句柄。 返回值是一handle。当错误发生时,返回null,此时用GetLastError函数可查看错误的信息。 利用CreateMutex()可以防止程序多个实例运行,如下例: Program ABC; Uses Forms,Windows,…; {$R *.res} Var hMutex:Hwnd; Begin Application.Initialize; hMutex:=CreateMutex(nil,False,Pchar(Application.Title)); if GetLastErrorERROR_ALREADY_EXISTS then begin //项目要运行的咚咚 end; ReleaseMutex(hMutex); Application.Run; End; 在本节的例程中,我们只是要防止线程进入同步代码区域中,所以lpName参数设置为nil。 3、在同步代码前用WaitForSingleObject()函数。该函数使得线程取得互斥对象(同步代码)的拥有权。该函数定义为: DWORD WINAPI WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds); 这个函数可以使当前线程在dwMilliseconds指定的时间内睡眠,直到hHandle参数指定的对象进入发信号状态为止。一个互斥对象不再被线程拥有时,它就进入发信号状态。当一个进程要终止时,它就进入发信号状态。dwMilliseconds参数可以设为0,这意味着只检查hHandle参数指定的对象是否处于发信号状态,而后立即返回。dwMilliseconds参数设为INFINITE,表示如果信号不出现将一直等下去。 这个函数的返回值含义: WAIT_ABANDONED 指定的对象是互斥对象,并且拥有这个互斥对象的线程在没有释放此对象之前就已终止。此时就称互斥对象被抛弃。这种情况下,这个互斥对象归当前线程所有,并把它设为非发信号状态 WAIT_OBJECT_0 指定的对象处于发信号状态 WAIT_TIMEOUT 等待的时间已过,对象仍然是非发信号状态 再次声明,当一个互斥对象不再被一个线程所拥有,它就处于发信号状态。此时首先调用WaitForSingleObject()函数的线程就成为该互斥对象的拥有者,此互斥对象设为不发信号状态。当线程调用ReleaseMutex()函数并传递一个互斥对象的句柄作为参数时,这种拥有关系就被解除,互斥对象重新进入发信号状态。 注意除WaitForSingleObject()函数外,你还可以使用WaitForMultipleObject()和MsgWaitForMultipleObject()函数,它们可以等待几个对象变为发信号状态。这两个函数的详细情况请看Win32 API联机文档。 4、在同步代码结束后,使用ReleaseMutex(THandle)函数来标志。该函数只是释放互斥对象和线程的拥有者关系,并不释放互斥对象的句柄。 5、调用CloseHandle(THandle)来关闭互斥对象。请注意例程中该函数的使用位置。 三、还有一种用信号量对象来管理线程同步的,它是在互斥的基础上建立的,但信号量增加了资源计数的功能,预定数目的线程允许同时进入要同步的代码。有点复杂,想不到在哪可以用,现在就不研究论了。 发表于 @ 2008年10月19日 00:47:00 | 评论( loading... ) | 编辑| 举报| 收藏 旧一篇:◆delphi多线程编程之二 ◆(乌龙哈里2008-10-12) | 新一篇:◆Delphi多线程编程之四 线程安全和VCL ◆(乌龙哈里2008-10-12)Csdn Blog version 3.1a Copyright © mygodsos

3,881

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 其它技术问题
社区管理员
  • 其它技术问题社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧