• C++多线程同步之Semaphore(信号量)


    一、线程间同步的几种方式

    从上篇博文中可以发现,当多个线程对同一资源进行使用时,会产生“争夺”的情况,为了避免这种情况的产生,也就出现了线程间的同步这个技术。线程间的同步有多种方式,在接下来的博文中我会依次介绍几种主流的同步方式,以及他们之间的区别。在本篇博文中将介绍使用信号量Semaphore达到线程间同步的目的。老规矩,所有代码都讲在win32平台和Linux平台下都实现一遍。

    相关函数和头文件

    //头文件
    #include <windows.h>
    
    //创建信号量API
    HANDLE WINAPI CreateSemaphore(
     _In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//指向SECURITY_ATTRIBUTES的指针;
     _In_     LONG                  lInitialCount,          //信号量对象的初始值;
     _In_     LONG                  lMaximumCount,  //信号量对象的最大值,这个值必须大于0;
     _In_opt_ LPCTSTR               lpName                 //信号量对象的名称;
    );
    
    //等待信号量API
    DWORD WINAPI WaitForSingleObject(
      _In_ HANDLE hHandle,          //信号量对象句柄
      _In_ DWORD  dwMilliseconds    //等待信号量时间,INFINET代表永久等待;
    );
    返回值:
    WAIT_ABANDONED(0x00000080L) 表示拥有信号量的线程再终止前未释放该信号量;
    WAIT_OBJECT_0(0x00000000L)  表示等到了信号量;
    WAIT_TIMEOUT(0x00000102L)   表示等待超时;
    WAIT_FAILED((DWORD)0xFFFFFFFF)  表示该函数执行失败,用GetLastError()得到错误码;  
    
    //释放信号量句柄
    BOOL WINAPI ReleaseSemaphore(
      _In_      HANDLE hSemaphore,         //信号量对象句柄;
      _In_      LONG   lReleaseCount,      //信号量释放的值,必须大于0;
      _Out_opt_ LPLONG lpPreviousCount     //前一次信号量值的指针,不需要可置为空;
    );
    返回值:成功返回非0;

    Win32平台下源码

    #include <iostream>
    #include <windows.h>
    using namespace std;
    
    HANDLE g_hSemaphore = NULL;                             //声明信号量变量
    
    unsigned long WINAPI Fun(LPVOID lpParamter)
    {
        int iRunTime = 0;
        //执行100次跳出
        while(++iRunTime<100)
        {
            WaitForSingleObject(g_hSemaphore, INFINITE);      //信号量值-1
            cout << "Fun() is running!"<<endl;
            ReleaseSemaphore(g_hSemaphore, 1, NULL);          //信号量值+1
            Sleep(10);
        }
        ExitThread(-1);
    }
    
    int main()
    {
        //创建信号量对象
        g_hSemaphore = CreateSemaphore(NULL          //信号量的安全特性
                                      , 1            //设置信号量的初始计数。可设置零到最大值之间的一个值
                                      , 1            //设置信号量的最大计数
                                      , NULL         //指定信号量对象的名称
                                      );
        if(NULL == g_hSemaphore)
        {
            cout << "create hSemaphore failed! error_code:"<<GetLastError()<<endl;
            return 0;
        }
    
        int iRunTime = 0;
        unsigned long ulThreadId = 0;
        //创建一个子线程
        HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, &ulThreadId);
    
        //执行100次跳出
        while(++iRunTime<100)
        {
            WaitForSingleObject(g_hSemaphore, INFINITE);   //信号量值-1
            cout << "main() is running, Thread id is " << ulThreadId <<endl;
            ReleaseSemaphore(g_hSemaphore, 1, NULL);       //信号量值+1
            Sleep(10);
        }
        system("pause");
        return 0;
    }

    执行结果: 
    这里写图片描述 
    可见未对屏幕资源产生“争夺”的情况,达到线程同步的目的。

    Linux平台

    相关函数和头文件

    int sem_init(sem_t *sem, int pshared, unsigned int value); 
    1)pshared==0 用于同一多线程的同步; 
    2)若pshared>0 用于多个相关进程间的同步(即由fork产生的); 
    int sem_getvalue(sem_t *sem, int *sval); 
    取回信号量sem的当前值,把该值保存到sval中。 
    若有1个或更多的线程或进程调用sem_wait阻塞在该信号量上,该函数返回两种值: 
    1) 返回0 
    2) 返回阻塞在该信号量上的进程或线程数目 
    linux采用返回的第一种策略。 
    sem_wait(或sem_trywait)相当于P操作,即申请资源。 
    int sem_wait(sem_t *sem); // 这是一个阻塞的函数 
    测试所指定信号量的值,它的操作是原子的。 
    若sem>0,那么它减1并立即返回。 
    若sem==0,则睡眠直到sem>0,此时立即减1,然后返回; 
    int sem_trywait(sem_t *sem); // 非阻塞的函数 
    其他的行为和sem_wait一样,除了: 
    若sem==0,不是睡眠,而是返回一个错误EAGAIN。 
    sem_post相当于V操作,释放资源。 
    int sem_post(sem_t *sem); 
    把指定的信号量sem的值加1; 
    呼醒正在等待该信号量的任意线程。

    源码

    #include <iostream>
    #include <pthread.h>
    #include <semaphore.h>
    using namespace std;
    
    static sem_t g_semaphore;
    static const int g_iRunTime = 5000;
    
    void* Fun(void* ptr)
    {
       int iRunTime = 0;
       while(++iRunTime< g_iRunTime)
       {
          sem_wait(&g_semaphore);
          cout<< "Fun() is running!" << endl;
          sem_post(&g_semaphore);
          usleep(100);
        }
    }
    
    
    int main()
    {
       pthread_t hHandle;
       sem_init(&g_semaphore, 0, 1);
       int iRet = pthread_create(&hHandle, NULL, Fun, NULL);        //create a thread;
       if(0 != iRet)
       {
           cout << "Create thread failed!" << endl;
       }
       sleep(1);
       int iRunTime = 0;
       while(++iRunTime<g_iRunTime)
       {
          sem_wait(&g_semaphore);
          cout << "main is running!" << endl;
          sem_post(&g_semaphore);
          usleep(100);
       }
       pthread_join(hHandle, NULL);
       return 0;
    }

    执行结果

    这里写图片描述 
    达到同步效果!

    关于Linux信号量

    Linux信号量比Windows要复杂,上述例子只是使用了其中最常用的一种,还有许多其他种类的信号量,后期会补上一篇关于Linux信号量详解的内容。

  • 相关阅读:
    LeetCode周赛#206
    CET-6备考丨词组、佳句积累
    界面设计9.24第一次课
    图像超分辨率重建
    OpenGL和计算机图形学初步认识
    OpenGL装gult库
    安装java
    vs2019配置Opengl
    最长上升子序列(最长递增子序列)LIS
    c++科学计数法 、long long的范围
  • 原文地址:https://www.cnblogs.com/lenmom/p/7998969.html
Copyright © 2020-2023  润新知