• windows多线程同步--临界区


    推荐参考博客:秒杀多线程第五篇 经典线程同步 关键段CS

     

    关于临界区的观念,一般操作系统书上面都有。

    适用范围:它只能同步一个进程中的线程,不能跨进程同步。一般用它来做单个进程内的代码快同步,效率比较高

    windows中与临界区有关的结构是 CRITICAL_SECTION,关于该结构体的内部结构可参考here

    使用时,主线程中要先初始化临界区,最后要删除临界区,具体使用见下面代码:

                                                                                  本文地址

    从一个例子来说明:假设有三个线程都需要使用打印机,我们可以把打印的代码放到临界区,这样就可以保证每次只有一个线程在使用打印机。

     

     #include<string>
     #include<iostream>
     #include<process.h>
     #include<windows.h>
     using namespace std;
    
     //定义一个临界区
     CRITICAL_SECTION g_cs;
    
    //线程绑定的函数返回值和参数是确定的,而且一定要__stdcall
    unsigned __stdcall threadFun(void *param)
    {
        EnterCriticalSection(&g_cs);//进入临界区,如果有其他线程则等待
        cout<<*(string *)(param)<<endl;
        LeaveCriticalSection(&g_cs);//退出临界区,其他线程可以进来了
        return 1;
    }
    
    
    int main()
    {
        //初始化临界区
        InitializeCriticalSection(&g_cs);
    
        HANDLE hth1, hth2, hth3;
        string s1 = "first", s2 = "second", s3 = "third";
    
        //创建线程
        hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
        hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
        hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL);
    
        //等待子线程结束
        WaitForSingleObject(hth1, INFINITE);
        WaitForSingleObject(hth2, INFINITE);
        WaitForSingleObject(hth3, INFINITE);
    
        //一定要记得关闭线程句柄
        CloseHandle(hth1);
        CloseHandle(hth2);
        CloseHandle(hth3);
    
        //删除临界区
        DeleteCriticalSection(&g_cs);
    }

     

    再看另外一个问题:编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推, 仿照文章windows多线程同步--信号量中的代码,我们把信号量替换成临界区。

     #include<string>
     #include<iostream>
     #include<process.h>
     #include<windows.h>
     using namespace std;
     //声明3个临界区
    CRITICAL_SECTION  g_cs1, g_cs2, g_cs3;
    
    //线程绑定的函数返回值和参数是确定的,而且一定要__stdcall
    unsigned __stdcall threadFunA(void *)
    {
        for(int i = 0; i < 10; i++){
            EnterCriticalSection(&g_cs1);//进入临界区
            cout<<"A";
            LeaveCriticalSection(&g_cs2);//离开临界区
        }
        return 1;
    }
    unsigned __stdcall threadFunB(void *)
    {
        for(int i = 0; i < 10; i++){
            EnterCriticalSection(&g_cs2);//进入临界区
            cout<<"B";
            LeaveCriticalSection(&g_cs3);//离开临界区
        }
        return 2;
    }
    unsigned __stdcall threadFunC(void *)
    {
        for(int i = 0; i < 10; i++){
            EnterCriticalSection(&g_cs3);//进入临界区
            cout<<"C";
            LeaveCriticalSection(&g_cs1);//离开临界区
        }
        return 3;
    }
    
    
    int main()
    {
        //初始化临界区
        InitializeCriticalSection(&g_cs1);
        InitializeCriticalSection(&g_cs2);
        InitializeCriticalSection(&g_cs3);
    
        HANDLE hth1, hth2, hth3;
    
        //创建线程
        hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFunA, NULL, 0, NULL);
        hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFunB, NULL, 0, NULL);
        hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFunC, NULL, 0, NULL);
    
        //等待子线程结束
        WaitForSingleObject(hth1, INFINITE);
        WaitForSingleObject(hth2, INFINITE);
        WaitForSingleObject(hth3, INFINITE);
    
        //一定要记得关闭线程句柄
        CloseHandle(hth1);
        CloseHandle(hth2);
        CloseHandle(hth3);
    
        //删除临界区
        DeleteCriticalSection(&g_cs1);
        DeleteCriticalSection(&g_cs2);
        DeleteCriticalSection(&g_cs3);
    }

     

    image

    为什么会这样呢,因为临界区有所有权的概念,即某个线程进入临界区后,就拥有该临界区的所有权,在他离开临界区之前,他可以无限次的再次进入该临界区,上例中线程A获得临界区1的所有权后,在线程C调用LeaveCriticalSection(&g_cs1)之前,A是可以无限次的进入临界区1的。利用信号量之所以可以实现题目的要求,是因为信号量没有所有权的概念,某个线程获得信号量后,如果信号量的值为0,那么他一定要等到信号量被释放时,才能再次获得

    关于临界区的详细解释清参考秒杀多线程第五篇 经典线程同步 关键段CS

     

    【版权声明】转载请注明出处http://www.cnblogs.com/TenosDoIt/p/3601308.html

  • 相关阅读:
    (4.25)Sqlserver中 登录用户只能看到自己拥有权限的库
    【查阅】mysql配置文件/参数文件重要参数笔录(my.cnf)
    【监控笔记】【2.5】DML(CDC)、DDL(DDL触发器)跟踪数据更改,数据库审计
    SQL Server 2008中的CDC(Change Data Capture)功能使用及释疑
    【监控笔记】【2.4】SQL Server中的 Ring Buffer 诊断各种系统资源压力情况
    【监控笔记】【2.3】扩展事件——慢查询SQL(执行超过3S的SQL)
    【监控笔记】【2.2】扩展事件——死锁监控
    最小配置启动SQL SERVER,更改SQL Server最大内存大小导致不能启动的解决方法
    【监控笔记】【2.1】扩展事件
    【扩展事件】跟踪超过3秒的SQL
  • 原文地址:https://www.cnblogs.com/TenosDoIt/p/3601308.html
Copyright © 2020-2023  润新知