使用互斥对象Mutex为什么不能实现线程同步?

作者在 2008-07-31 11:55:57 发布以下内容
    有过多线程开发经历的人,对于“互斥对象,临界区,事件,信号量”这四个对象肯定不会陌生。就我自己而言,它们真是"让我欢喜让我忧"。"让我欢喜" 因为利用它们往往能解决掉非常棘手的问题,例如经典的"生产者-消费者"问题,"让我忧"因为,我经常把它们搞混淆,用的时候不知道到底该用哪个好,比如我觉互斥对象和信号量这两者很相似,在解决"生产者-消费者"问题时,为了实现线程同步很多例子中都用了信号量,为什么不用互斥对象实现呢?我试图用互斥对象来做了一下,结果失败了。
 
    今天我决定解决掉这个问题。仔细阅读了MSDN上关于"Mutex,CriticalSection,Envent,Semaphore"的相关函数的介绍,特被是Create* 函数的介绍后,我终于弄清楚了这个问题;
    首先我给它们分成两组:
    Mutex和CriticalSection是用于实现数据的互斥的访问;
    Envent和Semaphore不但可以用于实现互斥,而且能够能与实现线程同步;
 
    究竟是什么原因导致Mutex不能实现线程同步呢?
    答案:Mutex在没有被任何线程所拥有时是有信号的,这时任何线程可以使用Wait functions去获取该对象的所有权。当Mutex被某个线程拥有后就处于没信号状态,并且只有当该线程使用ReleaseMutex释放该互斥对象时,它才能被其它线程获得。
     在一个线程中试图释放另个线程所拥有的Mutex是不会成功的,Mutex只能被所有者线程所释放。(这是主要原因)
 
   我们看看"生产者-消费者"问题:
   全局共享变量g_share相当于一个货架(一个只能摆放一件产品的货架)。初始情况下货架为空。只有当货架不为空时才允许消费者(DecProc)从货架上取走商品(减一操所),只有当货架上为空时才允许生产者(AddProc)往货架上放商品。生产者与消费者必须实现同步。否则就会出现不合逻辑的现象。
            
从上图我们可以看出在AddProc中需要对g_decSemaphore进行释放操作,在DecProc中需要对g_addSemaphore进行释放操作。但是由于Mutex的释放操作只能由所有者线程进程,在一个线程中释放其它线程所拥有的Mutex是不会成功的。正是这个原因,导致Mutex不能用于实现线程同步。而Envent,Semaphore和都可以在其它线程中设置其信号状态,所以他们能够用于实现线程同步。
使用信号量Semaphore进行线程同步的例子很多,下面我给出一个用Envent实现同步的例子:
 

 #include <windows.h>

int g_share=0;

HANDLE hmutex;

HANDLE g_addEvent;

HANDLE g_decEvent;

DWORD WINAPI AddProc(

  LPVOID lpParameter   // thread data

  );

DWORD WINAPI DecProc(

  LPVOID lpParameter   // thread data

  );

int main()

{

    SECURITY_ATTRIBUTES sa;

    DWORD dwStackSize=0;

    LPVOID lpParameter=NULL;

    DWORD dwCreationFlags=0;

    DWORD addThreadId;

    DWORD decThreadId;

    HANDLE addhandle;

    HANDLE dechandle;

 

    memset((BYTE *)&sa,0,sizeof(SECURITY_ATTRIBUTES));

    sa.nLength=sizeof(SECURITY_ATTRIBUTES);

  

//创建互斥对象实现g_share的互斥访问

    hmutex=CreateMutex(&sa,FALSE,NULL); 

   //创建事件实现进程同步

    g_addEvent=CreateEvent(&sa,FALSE,TRUE,NULL);

    g_decEvent=CreateEvent(&sa,FALSE,FALSE,NULL);

    addhandle=CreateThread(

              &sa,

              dwStackSize,

              AddProc,

              lpParameter,

              dwCreationFlags,

              &addThreadId);

   // Sleep(20);

    dechandle=CreateThread(

              &sa,

              dwStackSize,

              DecProc,

              lpParameter,

              dwCreationFlags,

              &decThreadId);

    WaitForSingleObject(addhandle,INFINITE);

    WaitForSingleObject(dechandle,INFINITE);

    CloseHandle(addhandle);

    CloseHandle(dechandle);

    return 0;

}

 

DWORD WINAPI AddProc(

  LPVOID lpParameter   // thread data

  )

{

    int loop=200;

    while(loop>0)

    {

        WaitForSingleObject(g_addEvent,INFINITE);

       loop-=1;

       WaitForSingleObject(hmutex,INFINITE);

       g_share+=1;

       printf("add proc loop: %d  g_share:%d\n",loop,g_share);

       Sleep(10);

        ReleaseMutex(hmutex);

       SetEvent(g_decEvent);

    }

    return 0;

};

 

DWORD WINAPI DecProc(

  LPVOID lpParameter   // thread data

  )

{

 

    int loop=200;

    while(loop>0)

    {

       WaitForSingleObject(g_decEvent,INFINITE);

       loop-=1;

       WaitForSingleObject(hmutex,INFINITE);

       g_share-=1;

       printf("dec proc loop: %d  g_share:%d\n",loop,g_share);

       Sleep(10);

       ReleaseMutex(hmutex);

       SetEvent(g_addEvent);

    }

    return 0;

};

 

             


本博客于即日起(2009.2.26)停止更新,


新博客地址:http://www.redicecn.cn ,

本博客的大部分文章已经转移到新博客中...


编程经验 | 阅读 15246 次
文章评论,共1条
laybor
2008-10-03 22:42
1
Wait Mutex后时其他线程不是像critical一样被锁在外面吗?Event与Mutex的差别是设置信号状态一个是随地可以设置,一个被限制权利的设置?
游客请输入验证码
浏览585012次