当前位置:C++技术网 > 资讯 > 你真的懂了互斥器的运行机制吗?

你真的懂了互斥器的运行机制吗?

更新时间:2015-11-09 14:21:32浏览次数:1+次


#include <windows.h> //包含头文件
#include <stdio.h>
    
DWORD WINAPI myfun1(LPVOID lpParameter);//声明线程函数
DWORD WINAPI myfun2(LPVOID lpParameter);
    
HANDLE hmutex;
int a=0;   //定义全局变量a
    
int main()
{
    hmutex=::CreateMutex(NULL,FALSE,NULL); //创建互斥对象并返回其句柄
    HANDLE h1,h2; //定义线程句柄
    h1=::CreateThread(NULL,0,myfun1,NULL,0,NULL); //创建线程1
    printf("线程1开始运行!\r\n");
    h2=::CreateThread(NULL,0,myfun2,NULL,0,NULL); //创建线程2
    printf("线程2开始运行!\r\n");
    ::CloseHandle(h1);  //关闭线程句柄对象
    ::CloseHandle(h2);
    ::Sleep(10000);    //程序睡眠10秒
    return 0;
}
    
DWORD WINAPI myfun1(LPVOID lpParameter)  //线程函数1
{
    while(1)
    {
        ::WaitForSingleObject(hmutex,INFINITE); //请求互斥对象
        if(a<10000)
        {
             a+=1;  //变量自加
             ::Sleep(1000);  //线程睡眠1秒
             printf("线程1:%d\r\n",a);
             ::ReleaseMutex(hmutex);  //释放互斥对象句柄
         }
         else
         {
             ::ReleaseMutex(hmutex);  //释放互斥对象句柄
             break;                                                   
         }
     }
     return 0; 
}
    
DWORD WINAPI myfun2(LPVOID lpParameter)  //线程函数2
{
    while(1)
    {
        ::WaitForSingleObject(hmutex,INFINITE); //请求互斥对象
        if(a<10000)
        {
            a+=1;
            ::Sleep(1000);
            printf("线程2:%d\r\n",a); //输出变量
            ::ReleaseMutex(hmutex);    //释放互斥对象句柄
         }
         else
         {
             ::ReleaseMutex(hmutex);   //释放互斥对象句柄
             break;                    //跳出循环
         }
    }
    return 0; //线程正常退出
}
这是利用互斥器来实现线程同步的,可是你确定你懂它的运行机制吗?那我们来说说吧。



HANDLE CreateMutex(
    LPSECURITY_ATTRIBUTES lpMutexAttributes, 安全属性结构指针,可为NULL,表示默认安全性
    BOOL bInitialOwner, //拥有互斥对象的线程(一般是主线程)是否占有该互斥量,TRUE:占有,FALSE:不占有
    LPCTSTR lpName //设置互斥对象的名字
); 

我们创建了一个互斥对象,并不让主线程(main函数)拥有它,当线程正在执行时,线程对象处于未激发状态。当线程结束时,线程对象就被激发了。因此,任何线程如果等待的是一个线程对象,将会在等待对象结束时被调用,因为当时线程对象自动变成激发状态。
一旦没有任何线程拥有 mu tex,这个 mu tex 便处于激发状态。因此,如
果没有任何线程拥有那个 mu tex, Wait...() 便会成功。反过来说,当线程拥有
mutex 时,它便不处于激发状态。如果有某个线程正在等待一个未被激发的
mutex,它便将进入“ blocking”(阻塞)状态。也就是说,该线程会停止执行,
直到 mu tex 被其拥有者释放并处于激发状态。这也就是我们为什么不让主线程拥有互斥器的原因,因为我们需要线程1拥有它。
下面是核心对象激发表的解释:


线程的执行是异步的,但是这里的同步机制-互斥器能让线程执行同步。在windows内核中,内核层包含了基本的操作系统原语和功能,控制线程和进程,线程调度,中断,同步对象等。内核层管理来你工作类型的对象,分发器对象和控制对象。分发器对象包含了各种同步功能,分发器对象包括事件,信号量,互斥器等,内核控制线程的切换(windows操作系统是实现各种线程的操作来完成任务的),内核根据调度规则来放弃或选择线程的执行状态,内核层中的分发器对象管理这些。那么在一个特定的一个线程分配到的运行时间就得受内核对象的控制,如果该内核对象没有满足它的激发条件,则该线程只得暂停,这就保证了线程的同步。
线程其实就是按照这种情节来执行的:
1,我们有一个mutex,此时没有任何线程拥有它,也就是说他处于激发状态。
2,某个线程调用waitforsingleobject()。
3,win32将这个互斥器的拥有权交给该线程。
4,Mutex立刻被设定为非激发状态。使得任何其他等待状态下的其他线程没有办法拥有其所有权。
5,获得该 mu tex 之线程调用 ReleaseMutex() ,将 mu tex 释放掉。于是循
环回到第一场景,周而复始。
而最后则是通过引用计数的计数值来删除核心对象-互斥器。随着程序代码的执行,CloseHandle(),或是ReleaseMutex,或是当线程结束时,mutex的引用计数就会减一。直到引用计数为0,最后才是销毁了它。
这就是大概的互斥器对象运行机制