多线程第五篇:互斥量Mutex

mutex用于线程或者进程间互斥.
mutex特点: 
     1.是内核对象,并记录所有使用者
     2.可以用于线程,进程间互斥.
     3.互斥对象名唯一标识互斥对象,名字相同,互斥对象必然相同,一对一映射.
mutex和临界区比较:
     1.临界区不能用于进程间互斥,他属于用户方式的对象,仅能用于线程间互斥,但是运行速度较快.而mutex是内核对象,速度较慢,但可以用于进程和线程间互斥.
     2.临界区和mutex一样,必须等到release才可以再次使用.
     3.由于mutex记录了每个使用他的对象,所以可以处理"遗弃"问题.

mutex函数:
CreateMutex:
HANDLE  CreateMutex(

  LPSECURITY_ATTRIBUTES   lpMutexAttributes,
  BOOL               bInitialOwner,    
  LPCTSTR         lpName
);
函数说明:

    第一个参数表示安全控制,一般直接传入NULL。

    第二个参数用来确定互斥量的初始拥有者。如果传入TRUE表示互斥量对象内部会记录创建它的线程的线程ID号并将递归计数设置为1,由于该线程ID非零,所以互斥量处于未触发状态。如果传入FALSE,那么互斥量对象内部的线程ID号将设置为NULL,递归计数设置为0,这意味互斥量不为任何线程占用,处于触发状态。第一个调用waitforsignalobject的线程获得所有权.

    第三个参数用来设置互斥量的名称,在多个进程中的线程就是通过名称来确保它们访问的是同一个互斥量。

函数返回值:

成功返回一个表示互斥量的句柄,失败返回NULL。

   
 第二个打开互斥量

函数原型:
HANDLE  OpenMutex(

DWORD    dwDesiredAccess,

BOOL    bInheritHandle,

LPCTSTR    lpName     //名称

);

函数说明:

第一个参数表示访问权限,对互斥量一般传入MUTEX_ALL_ACCESS。详细解释可以查看MSDN文档。

第二个参数表示互斥量句柄继承性,一般传入TRUE即可。

第三个参数表示名称。某一个进程中的线程创建互斥量后,其它进程中的线程就可以通过这个函数来找到这个互斥量。

函数返回值:

成功返回一个表示互斥量的句柄,失败返回NULL。

第三个触发互斥量

函数原型:
BOOL ReleaseMutex (HANDLE  hMutex)

函数说明:

访问互斥资源前应该要调用等待函数,结束访问时就要调用ReleaseMutex()来表示自己已经结束访问,其它线程可以开始访问了。



CloseHandle():清理互斥量

由于互斥量是内核对象,因此使用CloseHandle()就可以(这一点所有内核对象都一样)。

      由于互斥量常用于多进程之间的线程互斥,所以它比关键段还多一个很有用的特性——“遗弃”情况的处理。比如有一个占用互斥量的线程在调用 ReleaseMutex() 触发互斥量前就意外终止了(相当于该互斥量被“遗弃”了),那么所有等待这个互斥量的线程是否会由于该互斥量无法被触发而陷入一个无穷的等待过程中了?这显然不合理。因为占用某个互斥量的线程既然终止了那足以证明它不再使用被该互斥量保护的资源,所以这些资源完全并且应当被其它线程来使用。因此在这种“遗弃”情况下,系统自动把该互斥量内部的线程 ID 设置为 0 ,并将它的递归计数器复置为 0 ,表示这个互斥量被触发了。然后系统将 公平地 选定一个等待线程来完成调度(被选中的线程的 WaitForSingleObject() 会返回 WAIT_ABANDONED_0 )。

我们来看一下关于mutex的例子:

//按次序打印1-10号线程
#include <iostream>
#include <windows.h>
#include <process.h>

int g_count = 0;

HANDLE g_ThreadEvent ;
HANDLE g_ThreadMutex ;

unsigned int __stdcall ThreadFun(void *pthread_num)
{
    WaitForSingleObject( g_ThreadMutex,INFINITE );
    int num = *(( int*)pthread_num );
    std::cout<< "线程号:" <<GetCurrentThreadId()<< "线程编号为地址:" <<&num<< "线程编号" <<num<< "全局资源编号为:" <<++g_count<< std::endl ;
    SetEvent( g_ThreadEvent);
    ReleaseMutex( g_ThreadMutex);
                 return 0;
}

int main ()
{
    const int thread_num = 10;
    g_ThreadEvent = CreateEvent(NULL ,false,false,NULL);
    g_ThreadMutex = CreateMutex(NULL ,false,( LPCWSTR)"aaa" );
    HANDLE handle[thread_num ];

    for ( int i = 0 ; i<thread_num ; ++i ){
       // EnterCriticalSection(&g_csThreadParameter);
        handle[i ] = (HANDLE) _beginthreadex(NULL ,0,ThreadFun,& i,0,NULL );
        //WaitForSingleObject(g_ThreadEvent,INFINITE);
    }

    //等待所有创建的子线程都执行完.
    WaitForMultipleObjects( thread_num,handle ,true,INFINITE);
    CloseHandle( g_ThreadEvent);
    return 0;
}

实现了线程间互斥.

我们再来一个进程间互斥.
程序A:
//按次序打印1-10号线程
#include <iostream>
#include <windows.h>
#include <process.h>

int g_count = 0;

HANDLE g_ThreadEvent ;
HANDLE g_ThreadMutex ;

unsigned int __stdcall ThreadFun (void * pthread_num)
{
  
    WaitForSingleObject( g_ThreadMutex ,INFINITE );
    if ( g_count == 0 ){
        int a ;
        std ::cin >> a;
         ++ g_count ;
    }
    int num = *(( int *)pthread_num );
    //std::cout<<"线程号:"<<GetCurrentThreadId()<<"线程编号为地址:"<<&num<<"线程编号"<<num<<"全局资源编号为:"<<++g_count<<std::endl;
  
    std:: cout<< "进程号:" <<( int) getpid()<< "线程号:" << GetCurrentThreadId()<< std ::endl ;
    //SetEvent(g_ThreadEvent);
   
    ReleaseMutex( g_ThreadMutex );
                 return 0;
}

int main ()
{
    const int thread_num = 10;
    g_ThreadEvent = CreateEvent (NULL , false, false, NULL);
    g_ThreadMutex = CreateMutex (NULL , false,( LPCWSTR )"bbb" );
    HANDLE handle [thread_num ];
   
    for ( int i = 0 ; i <thread_num ; ++ i ){
       // EnterCriticalSection(&g_csThreadParameter);
        handle [i ] = ( HANDLE) _beginthreadex (NULL ,0, ThreadFun,& i ,0,NULL );
        //WaitForSingleObject(g_ThreadEvent,INFINITE);
    }

    //等待所有创建的子线程都执行完.
    WaitForMultipleObjects( thread_num ,handle , true, INFINITE);
    CloseHandle( g_ThreadEvent );
    CloseHandle( g_ThreadMutex );
    return 0;
}

然后执行:


此时会得到一个等待输入的框.

程序B:
//按次序打印1-10号线程
#include <iostream>
#include <windows.h>
#include <process.h>

int g_count = 0;

HANDLE g_ThreadEvent ;
HANDLE g_ThreadMutex ;

unsigned int __stdcall ThreadFun (void * pthread_num)
{
    WaitForSingleObject( g_ThreadMutex ,INFINITE );
    int num = *(( int *)pthread_num );
    //std::cout<<"线程号:"<<GetCurrentThreadId()<<"线程编号为地址:"<<&num<<"线程编号"<<num<<"全局资源编号为:"<<++g_count<<std::endl;

    std:: cout<< "进程号:" <<( int) getpid()<< "线程号:" << GetCurrentThreadId()<< std ::endl ;
    //SetEvent(g_ThreadEvent);
    ReleaseMutex( g_ThreadMutex );
  
    return 0;
}

int main ()
{
    const int thread_num = 10;
    g_ThreadEvent = CreateEvent (NULL , false, false, NULL);
    g_ThreadMutex = OpenMutex (MUTEX_ALL_ACCESS , true,( LPCWSTR) "bbb" );
    HANDLE handle [thread_num ];

    for ( int i = 0 ; i <thread_num ; ++ i ){
        // EnterCriticalSection(&g_csThreadParameter);
        handle [i ] = ( HANDLE) _beginthreadex (NULL ,0, ThreadFun,& i ,0,NULL );
        //WaitForSingleObject(g_ThreadEvent,INFINITE);
    }

    //等待所有创建的子线程都执行完.
    WaitForMultipleObjects( thread_num ,handle , true, INFINITE);
    CloseHandle( g_ThreadEvent );
    CloseHandle( g_ThreadMutex );
    return 0;
}
  执行:
此时也得到一个黑框:


我们在程序A中随便输入一个数字,会看到程序a的结果:


此时我们在看程序b:

也得到了结果.

好了,我们完成进程间互斥了.

思考:能不能用mutex来进行进程(线程)间同步呢,即控制线程执行的先后顺序?

我们来做个实验:
#include <iostream>
#include <windows.h>
#include <process.h>

int g_count = 0;

HANDLE g_ThreadEvent , g_threadMutex;

unsigned int __stdcall ThreadFun (void * pthread_num)
{
    //WaitForSingleObject(g_ThreadEvent,INFINITE);
   
    int num = *(( int *)pthread_num );
    //std::cout<<"线程号:"<<GetCurrentThreadId()<<"线程编号为地址:"<<&num<<"线程编号"<<num<<"全局资源编号为:"<<++g_count<<std::endl;

   std:: cout<< "线程编号" << num<< "全局资源编号为:" <<++ g_count<< std ::endl ;
    //std::cout<<"进程号:"<<(int)getpid()<<"线程号:"<<GetCurrentThreadId()<<std::endl;
    //SetEvent(g_ThreadEvent);
    ReleaseMutex( g_threadMutex );
                 return 0;
}

int main ()
{
    const int thread_num = 10;
 

    //g_ThreadEvent = CreateEvent(NULL,false,false,NULL);
    g_threadMutex = CreateMutex (NULL , false, NULL);
    //SetEvent(g_ThreadEvent);
    HANDLE handle [thread_num ];
   
    for ( int i = 0 ; i <thread_num ; ++ i ){
        handle [i ] = ( HANDLE) _beginthreadex (NULL ,0, ThreadFun,& i ,0,NULL );
        WaitForSingleObject (g_threadMutex , INFINITE);
    }

    //等待所有创建的子线程都执行完.
    WaitForMultipleObjects( thread_num ,handle , true, INFINITE);
    //CloseHandle(g_ThreadEvent);
    CloseHandle( g_threadMutex );
    return 0;
}
  
       由线程编号可知,mutex是不能进行线程间同步的,这是为什么呢?
       这是由于mutex是个内核对象,当用户想要使用它时候,必须等待其他用户释放,或者自己主动释放,而释放以后又会公平竞争,这样造成了一个循环,不能保证线程执行的先后顺序.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值