互斥体

1.等待函数
1)WaitForSingleObjectDWORD WaitForSingleObject(                
  HANDLE hHandle,        // handle to object                
  DWORD dwMilliseconds   // time-out interval                
);        
功能说明:                    
    等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止.                    
参数:                   
    hHandle    ->内核对象句柄,可以是进程也可以是线程. (窗体不是内核对象,进程和线程是内核对象)                   
    dwMilliseconds    ->等待时间,单位是毫秒  INFINITE(-1)一直等待  (毫秒数过后不管内核对象的状态,等待的线程继续往下走)                  
返回值:
    WAIT_OBJECT_0(0)            等待对象变为已通知        
    WAIT_TIMEOUT(0x102)            超时    
    (返回是那种原因导致等待的线程继续往下执行)     
特别说明(内核对象的状态):                    
    1】内核对象中的每种对象都可以说是处于已通知或未通知的状态之中                    
    2】这种状态的切换是由Microsoft为每个对象建立的一套规则来决定的                    
    3】当线程正在运行的时候,线程内核对象处于未通知状态                    
    4】当线程终止运行的时候,它就变为已通知状态                    
    5】在内核中就是个BOOL值,运行时FALSE 结束TRUE(也就是一个结构中的某个成员的值)    
注意:
    函数的第一个参数为内核对象;
    函数根据传入的内核对象的类型不同处理方式也不同;
    如果内核对象是进程或线程,在目标内核对象变成已通知状态后不会将其改为未通知状态;
    如果是其它内核对象就不一定了;
        例如:连续两次调用该函数;
        ::WaitForSingleObject(hThread1, INFINITE);
        ::WaitForSingleObject(hThread1, INFINITE);
        如果内核对象会在调用该函数后改为未通知状态,程序将卡死;
        原因:
            主线程第一次调用WaitForSingleObject等待线程1执行完;
            线程1执行完,状态变为已通知;
            但WaitForSingleObject将线程1的状态重新变为未通知;
            主线程第二次调用WaitForSingleObject等待线程1变为已通知;
            此时线程1已经执行完,不可能再主动变为已通知状态了;
            主线程一直等待下去;
实例:
DWORD WINAPI ThreadProc1(LPVOID lpParameter)                    
{                    
    for(int i=0;i<5;i++)                
    {                
        printf("+++++++++\n");            
        Sleep(1000);            
    }                
    return 0;                
}                    
                    
int main(int argc, char* argv[])                    
{                    
    //创建一个新的线程                
    HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1,                 
        NULL, 0, NULL); 
    //主线程将等待线程1进入已通知状态后才能继续运行;线程1运行完后自动进入已通知状态;也就是说输出5次+++++++++后才弹框;      
    DWORD dwCode = ::WaitForSingleObject(hThread1, INFINITE);                
    MessageBox(0,0,0,0);                
    return 0;                
}                    

 

2)WaitForMultipleObjects
DWORD WaitForMultipleObjects(                        
  DWORD nCount,             // number of handles in array                        
  CONST HANDLE *lpHandles,  // object-handle array                        
  BOOL bWaitAll,            // wait option                        
  DWORD dwMilliseconds      // time-out interval                        
);
功能说明:                            
    同时查看若干个内核对象的已通知状态                            
参数:                            
    nCount    ->要查看内核对象的数量                            
    lpHandles    ->内核对象数组                            
    bWaitAll    ->等到类型  TRUE 等到所有变为已通知  FALSE 只要有一个变为已通知                            
    dwMilliseconds    ->超时时间 ;INFINITE一直等待                            
返回值:                            
    bWaitAll为TRUE时,返回WAIT_OBJECT_0(0) 代码所以内核对象都变成已通知                            
    bWaitAll为FALSE时,返回最先变成已通知的内核对象在数组中的索引                            
    WAIT_TIMEOUT(0x102)            超时                
实例:
DWORD WINAPI ThreadProc1(LPVOID lpParameter)                            
{                            
    for(int i=0;i<5;i++)                        
    {                        
        printf("+++++++++\n");                    
        Sleep(1000);                    
    }                        
    return 0;                        
}                            
                            
DWORD WINAPI ThreadProc2(LPVOID lpParameter)                            
{                            
    for(int i=0;i<3;i++)                        
    {                        
        printf("---------\n");                    
        Sleep(1000);                    
    }                        
                            
    return 0;                        
}                            
                            
int main(int argc, char* argv[])                            
{                            
    HANDLE hArray[2];                        
    //创建一个新的线程                        
    HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1,                         
        NULL, 0, NULL);                    
    //创建一个新的线程                        
    HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2,                         
        NULL, 0, NULL);                    
                            
    hArray[0] = hThread1;                        
    hArray[1] = hThread2;                        
                            
    DWORD dwCode = ::WaitForMultipleObjects(2, hArray,FALSE,INFINITE);     //等到其中一个线程执行完变成已通知状态后弹框;                   
    MessageBox(0,0,0,0);                        
    return 0;                        
}                            

 

2.互斥体
互斥:
    一个全局变量或资源,可能有多个线程同时访问时会出现线程安全问题;
    可以用互斥机制来解决:一个线程正访问目标资源时其它线程都不能访问;
    临界区就是实现互斥的一种方式;
 
互斥体也是用来实现互斥的一种方式;
 
1)创建互斥体
api函数:
HANDLE CreateMutex(
  LPSECURITY_ATTRIBUTES lpMutexAttributes,  // SD
  BOOL bInitialOwner,                       // initial owner
  LPCTSTR lpName                            // object name
);
lpMutexAttributes    ->三环程序一般传NULL;如果这个参数不为空一般就是内核对象;
bInitialOwner    ->
lpName    ->互斥体名称;如果另一个进程想打开该互斥体,需要用到
 
2)打开互斥体
HANDLE OpenMutex(
  DWORD dwDesiredAccess,  // access
  BOOL bInheritHandle,    // inheritance option
  LPCTSTR lpName          // object name
);
dwDesiredAccess    ->权限;MUTEX_ALL_ACCESS、SYNCHRONIZE
bInheritHandle    ->返回的句柄是否可继承
lpName    ->目标互斥体名
 
3)使用互斥体
用在进程中通信时;
和等待函数配合使用;当一个进程访问互斥体时,其它进程无法访问同一名称的互斥体;
进程一:
HANDLE g_hMutex = CreateMutex(NULL,FALSE, "XYZ");
 
进程二:
HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ");
WaitForSingleObject(g_hMutex,INFINITE);
//逻辑代码
ReleaseMutex(g_hMutex);
 
进程三:
HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ");
WaitForSingleObject(g_hMutex,INFINITE);
//逻辑代码
ReleaseMutex(g_hMutex);

 

4)互斥体与临界区的区别:
    1】临界区只能用于单个进程间的线程控制.
    2】互斥体可以设定等待超时,但临界区不能.
    3】线程意外终结时,Mutex可以避免无限等待.
    4】Mutex效率没有临界区高.
 
互斥体是内核对象;
内核对象是在程序的低2G内存中创建的;
进程可以共享内核对象,因此互斥体能跨进程通信;
 
3.实例
目标:创建一个抢红包程序
第一步:在第一个文本框中输入一个值,比如1000                    
第二步:点击抢红包,同时创建3个线程,每个线程循环进行抢红包的操作,每次抢50                    
第三步:使用Mutex进行线程控制,当第一个文本框中的值<50时,强红包线程结束.                    
                    
特别说明:                    
1】四个文本框中的值总和应该为1000                    
2】强红包线程每次延时50毫秒. (如果不延时,其中一个线程可能瞬间抢完);                   
3】使用WaitForMultipleObjects监听所有线程,当线程全部结束后 ;                   
   调用CloseHandle关闭句柄. (线程运行完后自动结束,互斥体需要自己来关);   
    等待函数不能直接在WinMain的按钮点击事件中调用,否则主函数将等待导致无法接受消息循环而卡死;
    解决办法是在按钮点击事件中创建一个新的线程,让新线程来创建和控制三个红包线程;                
 
 
用临界区实现互斥:
#include<windows.h>
#include<stdio.h>
#include "resource.h"
 
HWND hongbao;
HWND zg;
HWND wst;
HWND smy;
 
CRITICAL_SECTION cs;
DWORD num;
 
//zg
DWORD WINAPI ThreadProc1(                
             LPVOID lpParameter          
             ){
    while(num > 0){
        EnterCriticalSection(&cs);
 
        //获取文本框内容
        TCHAR szBuffer[10];
        memset(szBuffer, 0, 10);
        GetWindowText(hongbao, szBuffer, 10);
 
        TCHAR zgBuffer[10];
        memset(zgBuffer, 0, 10);
        GetWindowText(zg, zgBuffer, 10);
    
        //字符串转数字
        sscanf(szBuffer, "%d", &num);
        DWORD zgNum;
        sscanf(zgBuffer, "%d", &zgNum);
        if(num > 0){
            memset(szBuffer, 0, 10);
            memset(zgBuffer, 0, 10);
            
            sprintf(szBuffer, "%d", --num);
            sprintf(zgBuffer, "%d", ++zgNum);
            SetWindowText(hongbao, szBuffer);
            SetWindowText(zg, zgBuffer);
        }
        LeaveCriticalSection(&cs);
        Sleep(10);
    }
 
    return 0;
}
//wst
DWORD WINAPI ThreadProc2(                
             LPVOID lpParameter          
             ){
    while(num > 0){
        EnterCriticalSection(&cs);
 
        //获取文本框内容
        TCHAR szBuffer[10];
        memset(szBuffer, 0, 10);
        GetWindowText(hongbao, szBuffer, 10);
 
        TCHAR wstBuffer[10];
        memset(wstBuffer, 0, 10);
        GetWindowText(wst, wstBuffer, 10);
    
        //字符串转数字
        sscanf(szBuffer, "%d", &num);
        DWORD wstNum;
        sscanf(wstBuffer, "%d", &wstNum);
        if(num > 0){
            memset(szBuffer, 0, 10);
            memset(wstBuffer, 0, 10);
            
            sprintf(szBuffer, "%d", --num);
            sprintf(wstBuffer, "%d", ++wstNum);
            SetWindowText(hongbao, szBuffer);
            SetWindowText(wst, wstBuffer);
        }
        LeaveCriticalSection(&cs);
        Sleep(10);
        
    }
 
    return 0;
}
//smy
DWORD WINAPI ThreadProc3(                
             LPVOID lpParameter          
             ){
    while(num > 0){
        EnterCriticalSection(&cs);
 
        //获取文本框内容
        TCHAR szBuffer[10];
        memset(szBuffer, 0, 10);
        GetWindowText(hongbao, szBuffer, 10);
 
        TCHAR smyBuffer[10];
        memset(smyBuffer, 0, 10);
        GetWindowText(smy, smyBuffer, 10);
    
        //字符串转数字
        sscanf(szBuffer, "%d", &num);
        DWORD smyNum;
        sscanf(smyBuffer, "%d", &smyNum);
        if(num > 0){
            memset(szBuffer, 0, 10);
            memset(smyBuffer, 0, 10);
            
            sprintf(szBuffer, "%d", --num);
            sprintf(smyBuffer, "%d", ++smyNum);
            SetWindowText(hongbao, szBuffer);
            SetWindowText(smy, smyBuffer);
        }
        LeaveCriticalSection(&cs);
        Sleep(10);
    }
 
    return 0;
}
 
//线程函数
DWORD WINAPI ThreadProc(                
             LPVOID lpParameter   // 给线程传递参数       
             ){
    InitializeCriticalSection(&cs);
 
    //创建线程
    HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);                                   
    HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);                                        
    HANDLE hThread3 = ::CreateThread(NULL, 0, ThreadProc3, NULL, 0, NULL);
    return 0;
}
 
//回调函数
BOOL CALLBACK DialogProc(                                    
                         HWND hwndDlg,  // handle to dialog box            
                         UINT uMsg,     // message            
                         WPARAM wParam, // first message parameter            
                         LPARAM lParam  // second message parameter            
                         )            
{    
    switch(uMsg)                                
    {    
    case WM_INITDIALOG :
        hongbao = GetDlgItem(hwndDlg, IDC_HB);
        zg = GetDlgItem(hwndDlg, TXT_ZG);
        wst = GetDlgItem(hwndDlg, TXT_WST);
        smy = GetDlgItem(hwndDlg, TXT_SMY);
 
        SetWindowText(zg, TEXT("0"));
        SetWindowText(wst, TEXT("0"));
        SetWindowText(smy, TEXT("0"));
        return TRUE;     
                                    
    case  WM_COMMAND :                                                                   
        switch (LOWORD (wParam))                            
        {
        case IDC_BTN:  
            //获取文本框内容
            TCHAR szBuffer[10];
            memset(szBuffer, 0, 10);
            GetWindowText(hongbao, szBuffer, 10);
            //字符串转数字
            sscanf(szBuffer, "%d", &num);
 
            //清空输入框
            SetWindowText(zg, TEXT("0"));
            SetWindowText(wst, TEXT("0"));
            SetWindowText(smy, TEXT("0"));
 
            HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);        
            //如果不在其他的地方引用它 关闭句柄                
            ::CloseHandle(hThread);            
            return TRUE;  
 
        return FALSE;    
        }                            
        break ;   
    case WM_CLOSE:
        EndDialog(hwndDlg, 0);
        return TRUE;        
    }                                    
                                    
    return FALSE ;                                
}     
 
//程序入口
int CALLBACK WinMain(
                     HINSTANCE hInstance,
                     HINSTANCE hPrevHinstance,
                     LPSTR lpCmdLine,
                     int nCmdShow
        ){  
    //创建对话框窗口
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc);    
    
    return 0;
}

 

用互斥体实现互斥:
#include<windows.h>
#include<stdio.h>
#include "resource.h"
 
HWND hongbao;
HWND zg;
HWND wst;
HWND smy;
 
HANDLE g_hMutex;
DWORD num;
 
//zg
DWORD WINAPI ThreadProc1(                
             LPVOID lpParameter          
             ){
    while(num > 0){
        WaitForSingleObject(g_hMutex,INFINITE);    //等待互斥体
 
 
        //获取文本框内容
        TCHAR szBuffer[10];
        memset(szBuffer, 0, 10);
        GetWindowText(hongbao, szBuffer, 10);
 
        TCHAR zgBuffer[10];
        memset(zgBuffer, 0, 10);
        GetWindowText(zg, zgBuffer, 10);
    
        //字符串转数字
        sscanf(szBuffer, "%d", &num);
        DWORD zgNum;
        sscanf(zgBuffer, "%d", &zgNum);
        if(num > 0){
            memset(szBuffer, 0, 10);
            memset(zgBuffer, 0, 10);
            
            sprintf(szBuffer, "%d", --num);
            sprintf(zgBuffer, "%d", ++zgNum);
            SetWindowText(hongbao, szBuffer);
            SetWindowText(zg, zgBuffer);
        }
 
        ReleaseMutex(g_hMutex);    //释放互斥体
        Sleep(10);
    }
    return 0;
}
 
//wst
DWORD WINAPI ThreadProc2(                
             LPVOID lpParameter          
             ){
    while(num > 0){
        WaitForSingleObject(g_hMutex,INFINITE);    //等待互斥体
 
        //获取文本框内容
        TCHAR szBuffer[10];
        memset(szBuffer, 0, 10);
        GetWindowText(hongbao, szBuffer, 10);
 
        TCHAR wstBuffer[10];
        memset(wstBuffer, 0, 10);
        GetWindowText(wst, wstBuffer, 10);
    
        //字符串转数字
        sscanf(szBuffer, "%d", &num);
        DWORD wstNum;
        sscanf(wstBuffer, "%d", &wstNum);
        if(num > 0){
            memset(szBuffer, 0, 10);
            memset(wstBuffer, 0, 10);
            
            sprintf(szBuffer, "%d", --num);
            sprintf(wstBuffer, "%d", ++wstNum);
            SetWindowText(hongbao, szBuffer);
            SetWindowText(wst, wstBuffer);
        }
        ReleaseMutex(g_hMutex);    //释放互斥体
        Sleep(10);
    }
    return 0;
}
 
//smy
DWORD WINAPI ThreadProc3(                
             LPVOID lpParameter          
             ){
    while(num > 0){
        WaitForSingleObject(g_hMutex,INFINITE);    //等待互斥体
 
        //获取文本框内容
        TCHAR szBuffer[10];
        memset(szBuffer, 0, 10);
        GetWindowText(hongbao, szBuffer, 10);
 
        TCHAR smyBuffer[10];
        memset(smyBuffer, 0, 10);
        GetWindowText(smy, smyBuffer, 10);
    
        //字符串转数字
        sscanf(szBuffer, "%d", &num);
        DWORD smyNum;
        sscanf(smyBuffer, "%d", &smyNum);
        if(num > 0){
            memset(szBuffer, 0, 10);
            memset(smyBuffer, 0, 10);
            
            sprintf(szBuffer, "%d", --num);
            sprintf(smyBuffer, "%d", ++smyNum);
            SetWindowText(hongbao, szBuffer);
            SetWindowText(smy, smyBuffer);
        }
        ReleaseMutex(g_hMutex);    //释放互斥体
        Sleep(10);
    }
    return 0;
}
 
 
//线程函数
DWORD WINAPI ThreadProc(                
             LPVOID lpParameter   // 给线程传递参数       
             ){
    g_hMutex = CreateMutex(NULL,FALSE, "XYZ");    //创建互斥体
    
    HANDLE hArray[3];
    //创建线程
    HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);                                   
    HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);                                        
    HANDLE hThread3 = ::CreateThread(NULL, 0, ThreadProc3, NULL, 0, NULL);
    
    //等待所有线程执行完后关闭互斥体
    hArray[0] = hThread1;                        
    hArray[1] = hThread2;
    hArray[2] = hThread3;
    DWORD dwCode = ::WaitForMultipleObjects(3, hArray,FALSE,INFINITE);
    ::CloseHandle(g_hMutex);
 
    return 0;
}
 
 
//回调函数
BOOL CALLBACK DialogProc(                                    
                         HWND hwndDlg,  // handle to dialog box            
                         UINT uMsg,     // message            
                         WPARAM wParam, // first message parameter            
                         LPARAM lParam  // second message parameter            
                         )            
{    
    switch(uMsg)                                
    {    
    case WM_INITDIALOG :
        hongbao = GetDlgItem(hwndDlg, IDC_HB);
        zg = GetDlgItem(hwndDlg, TXT_ZG);
        wst = GetDlgItem(hwndDlg, TXT_WST);
        smy = GetDlgItem(hwndDlg, TXT_SMY);
 
        SetWindowText(zg, TEXT("0"));
        SetWindowText(wst, TEXT("0"));
        SetWindowText(smy, TEXT("0"));
        return TRUE;     
                                    
    case  WM_COMMAND :                                                                   
        switch (LOWORD (wParam))                            
        {
        case IDC_BTN:  
            //获取文本框内容
            TCHAR szBuffer[10];
            memset(szBuffer, 0, 10);
            GetWindowText(hongbao, szBuffer, 10);
            //字符串转数字
            sscanf(szBuffer, "%d", &num);
 
            //清空输入框
            SetWindowText(zg, TEXT("0"));
            SetWindowText(wst, TEXT("0"));
            SetWindowText(smy, TEXT("0"));
 
            HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);        
            //如果不在其他的地方引用它 关闭句柄                
            ::CloseHandle(hThread);            
            return TRUE;  
 
        return FALSE;    
        }                            
        break ;   
    case WM_CLOSE:
        EndDialog(hwndDlg, 0);
        return TRUE;        
    }                                    
                                    
    return FALSE ;                                
}     
 
 
//程序入口
int CALLBACK WinMain(
                     HINSTANCE hInstance,
                     HINSTANCE hPrevHinstance,
                     LPSTR lpCmdLine,
                     int nCmdShow
        ){  
    //创建对话框窗口
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc);    
    
    return 0;
}

 

 
        
 
 
posted @ 2020-01-02 14:10  L丶银甲闪闪  阅读(703)  评论(0编辑  收藏  举报