• 关键段与互斥量的陷阱


      在下有理解不到位,或是有更好的建议,欢迎批评指正!

      相同点:关键段和互斥量都可以用来控制线程互斥访问资源。

      不同点:关键段只能用于单进程间的多线程互斥,而互斥量可以用于多进程间的多线程互斥,而且互斥量可以处理“遗弃”(即某个个进程的某个线程占用了互斥量,但是它因为某些原因非正常关闭了,互斥量也没有释放,这是系统就是检测,处理这种情况,释放互斥量,以免其他线程一直等待下去)的问题。

        

      按这样理解的话,在单个进程内使用关键段与互斥量应该可以达到类似的结果,真的是这样子吗?

      个人用比较常见的生成者消费者模型稍作变型,成了4个生产者,4个消费者,4个临界区资源:

      用信号量full,empty表示临界区的资源,用关键段处理对变量的互斥访问。

    #include<stdio.h>
    #include<process.h>
    #include<windows.h>
    
    volatile  long g_nLoginCount;
    const int THREAD_NUM = 10;
    volatile  long g_num;
    
    HANDLE g_Mutex;
    
    
    HANDLE g_Semaphore_full,g_Semaphore_empty; //信号量
    
    CRITICAL_SECTION g_thread;
    
    int num=10;
    
    unsigned int __stdcall producer(void *pPM){
    
        int i;
        for(i=0;i<4;i++){
            Sleep(100);
            WaitForSingleObject(g_Semaphore_empty,INFINITE);
            Sleep(100);
    
            EnterCriticalSection(&g_thread); //用关键段不会有问题
        //    WaitForSingleObject(g_Mutex,INFINITE); //用互斥量就会出问题
            g_num++;
            printf("生产者ID:%d 累积SUM : %d
    ",GetCurrentThreadId(),g_num);
        //    ReleaseMutex(&g_Mutex);
            LeaveCriticalSection(&g_thread);
    
            ReleaseSemaphore(g_Semaphore_full,1,NULL);//信号量++
            Sleep(100);
        }
        return 0;
    }
    
    unsigned int __stdcall customer(void *pPM){
        int ok=1;
        while(1){
            //Sleep(0);
            Sleep(100);
            Sleep(0);
            WaitForSingleObject(g_Semaphore_full,INFINITE);
    
            printf("        消费者ID:%d 累积SUM : %d
    ",GetCurrentThreadId(),g_num);
    
            ReleaseSemaphore(g_Semaphore_empty,1,NULL);//信号量++
        }
        return 0;
    
    }
    
    int main(){
    
        g_Semaphore_full = CreateSemaphore(NULL,0,4,NULL);//当前0个资源,最大允许4个同时访
        g_Semaphore_empty = CreateSemaphore(NULL,0,4,NULL);//当前0个资源,最大允许4个同时访
        g_Mutex = CreateMutex(NULL,FALSE,NULL);
        InitializeCriticalSection(&g_thread);
    
        HANDLE handle[10];
    
        ReleaseSemaphore(g_Semaphore_empty,4,NULL);//信号量++
    
    
        int i;
    
        for(i=0;i<4;i++){
            handle[i] = (HANDLE)_beginthreadex(NULL,0,producer,NULL,0,NULL);
        }
        for(i=0;i<4;i++){
            handle[i+4] = (HANDLE)_beginthreadex(NULL,0,customer,NULL,0,NULL);
        }
    
        WaitForMultipleObjects(THREAD_NUM,handle,TRUE,INFINITE);
    
        getchar(); //一定要在这里设置使主线程停止,否则执行到后面的话,子线程就被关闭了
    
        for(i=0;i<6;i++)
        CloseHandle(handle[i]);
        CloseHandle(g_Semaphore_full);
        CloseHandle(g_Semaphore_empty);
        CloseHandle(g_Mutex);
    
        return 0;
    }

    这样是正常的。

    但是用互斥量处理对g_num变量的互斥访问的时候:

    #include<stdio.h>
    #include<process.h>
    #include<windows.h>
    
    volatile  long g_nLoginCount;
    const int THREAD_NUM = 10;
    volatile  long g_num;
    
    HANDLE g_Mutex;
    
    
    HANDLE g_Semaphore_full,g_Semaphore_empty; //信号量
    
    CRITICAL_SECTION g_thread;
    
    int num=10;
    
    unsigned int __stdcall producer(void *pPM){
    
        int i;
        for(i=0;i<4;i++){
            Sleep(100);
            WaitForSingleObject(g_Semaphore_empty,INFINITE);
            Sleep(100);
    
        //    EnterCriticalSection(&g_thread); //用关键段不会有问题
            WaitForSingleObject(g_Mutex,INFINITE); //用互斥量就会出问题
            g_num++;
            printf("生产者ID:%d 累积SUM : %d
    ",GetCurrentThreadId(),g_num);
            ReleaseMutex(&g_Mutex);
        //    LeaveCriticalSection(&g_thread);
    
            ReleaseSemaphore(g_Semaphore_full,1,NULL);//信号量++
            Sleep(100);
        }
        return 0;
    }
    
    unsigned int __stdcall customer(void *pPM){
        int ok=1;
        while(1){
            //Sleep(0);
            Sleep(100);
            Sleep(0);
            WaitForSingleObject(g_Semaphore_full,INFINITE);
    
            printf("        消费者ID:%d 累积SUM : %d
    ",GetCurrentThreadId(),g_num);
    
            ReleaseSemaphore(g_Semaphore_empty,1,NULL);//信号量++
        }
        return 0;
    
    }
    
    int main(){
    
        g_Semaphore_full = CreateSemaphore(NULL,0,4,NULL);//当前0个资源,最大允许4个同时访
        g_Semaphore_empty = CreateSemaphore(NULL,0,4,NULL);//当前0个资源,最大允许4个同时访
        g_Mutex = CreateMutex(NULL,FALSE,NULL);
        InitializeCriticalSection(&g_thread);
    
        HANDLE handle[10];
    
        ReleaseSemaphore(g_Semaphore_empty,4,NULL);//信号量++
    
    
        int i;
    
        for(i=0;i<4;i++){
            handle[i] = (HANDLE)_beginthreadex(NULL,0,producer,NULL,0,NULL);
        }
        for(i=0;i<4;i++){
            handle[i+4] = (HANDLE)_beginthreadex(NULL,0,customer,NULL,0,NULL);
        }
    
        WaitForMultipleObjects(THREAD_NUM,handle,TRUE,INFINITE);
    
        getchar(); //一定要在这里设置使主线程停止,否则执行到后面的话,子线程就被关闭了
    
        for(i=0;i<6;i++)
        CloseHandle(handle[i]);
        CloseHandle(g_Semaphore_full);
        CloseHandle(g_Semaphore_empty);
        CloseHandle(g_Mutex);
    
        return 0;
    }

    观察生产者的ID:成了有序的情况,这就是说,它使得一个线程完全执行完了之后在执行另外一个线程,这跟关键段的作用也差的太大了!!到底是什么情况??

  • 相关阅读:
    C#语法相比其它语言比较独特的地方
    Git源码管理工具使用
    2018年第九届蓝桥杯题目(C/C++B组)汇总
    Unity插件系列之二维码
    在本机使用虚拟机安装一个linux系统,并搭建ftp服务器
    用UE4蓝图制作FPS_零基础学虚幻4第二季
    【坦克大战】Unity3D多人在线游戏(泰课的坦克大战--旋转的螺丝钉)
    Drag(拖拽)和Move(移动)两个脚本
    解决:Word在试图打开文件时遇到错误
    2018年的一些记录,共勉
  • 原文地址:https://www.cnblogs.com/huhuuu/p/3589590.html
Copyright © 2020-2023  润新知