• 多线程(六)多线程同步_SemaPhore信号量


    信号量依然是一种内核同步对象,它的作用在于控制共享资源的最大访问数量

    例如:我们有一个服务器,为这服务器创建一个线程池,线程池有五个线程,每个线程处理1个请求。当五个线程都在处理请求时,这个线程池己到达使用上限,

    可使用数量为0,无法再处理其它请求。此时又有新的请求到来,新的请求将被放入缓存中进行等待。当有线程处理完请求退出时,线程池可使用数量+1。

    期间线程池会一直判断可使用数量是否大于0并且小于最大使用数量5?如果为真,会查找缓存中是否有等待的请求,如果有就取出一个请求进行处理。

    整个线程池周而复始执行相应操作,始终保持着同时只能处理五个请求。

    包含三个部份:

    使用计数器:所有内存对象都有这个属性

    最大资源计数器: 控制所能访问资源的最大数量

    当前资源计数器当前可以使用的资源数量

    注意:当前资源计数永远不可能大于最大资源计数,也不会是个负数。当前资源计数为0时,信号量是未触发状态。当前资源计数大于0时,信号是触发状态

    线程中调用等待函数时,等待函数内部会检查当前资源计数器,如果发现当前资源计数器为0是未触发状态,线程就会处于等待状态。

    如果发现当前资源计数器>0,就会把资源计数器-1,然后使当前线程变为可调度状态。线程执行完相关代码后需要调用ReleaseSemphore增加当前可以使用的资源数量

    创建信号对象:

    HANDLE CreateSemaphore(PSECURITY_ATTRIBUTE psa,LONG lInitialCount,LONG lMaximumCount,PCTSTR pszName);

    1.psa   指向安全属性指针,一般为NULL

    2. lInitialCount   初始时有多少资源可供使用,一般=lMaximumCount

    3. lMaximumCount  所能访问资源的最大数量

    4.pszName  信号对象名称,如果不跨进程使用,一般为NULL

    返回值:

    成功返回新信号对象句柄,失败返回0

    释放信号对象:增加当前可以使用的资源数量

    BOOL ReleaseSemaphore(HANDLE hsem, LONG lReleaseCount,PLONG plPreviousCount);

    1.hsem  信号对象句柄

    2.lReleaseCount  改变的计数值,一般为1,当前资源计数器会+1

    3.plPreviousCount  返回当前资源数量的原始值,一般为NULL

    编写一个Demo用于演示SemaPhore基本操作

    功能介绍:

    模拟公司员工上网系统,一共有10名员工,同时只允许3名员工上网,每人上网时间为5秒

    开始编写代码:

    1. 创建个基于对话框的工程SemaPhoreDemo

    2. 添加一个富文本框用于显示信息,修改属性支持多行,并且为只读. 然后绑定一个变量

    3. 定义相关全局变量和线程函数前置声明

     1 // CSemaPhoreDlg 对话框
     2 #define WM_UPDATAEDIT WM_USER+100  //自定义消息用来更新编辑框
     3 
     4 HANDLE g_hSemaPhore; //信号对象句柄
     5 HWND g_hwndMain; //当前窗口句柄
     6 
     7 //线程函数前置声明
     8 DWORD WINAPI Thread_One(LPVOID lpParam);
     9 DWORD WINAPI Thread_Two(LPVOID lpParam);
    10 DWORD WINAPI Thread_Three(LPVOID lpParam);
    11 DWORD WINAPI Thread_Four(LPVOID lpParam);
    12 DWORD WINAPI Thread_Five(LPVOID lpParam);
    13 DWORD WINAPI Thread_Six(LPVOID lpParam);
    14 DWORD WINAPI Thread_Seven(LPVOID lpParam);
    15 DWORD WINAPI Thread_Eight(LPVOID lpParam);
    16 DWORD WINAPI Thread_Nine(LPVOID lpParam);
    17 DWORD WINAPI Thread_Ten(LPVOID lpParam);
    相关全局变量和线程函数的前置声明

    4.窗口添加个消息响应_用于更新编辑框信息

     1 afx_msg LRESULT CSemaPhoreDlg::OnUpdataedit(WPARAM wParam, LPARAM lParam)
     2 {
     3 
     4     m_richEdit.UpdateData(TRUE);
     5     CString str;
     6     m_richEdit.GetWindowText(str);
     7     str += (LPCTSTR)wParam;
     8     str += _T("
    ");
     9     m_richEdit.SetWindowText(str);
    10 
    11     //开始检测每一行文本,只要有"结束"字符串就把该行用红色显示
    12     CHARFORMAT cf;
    13     ZeroMemory(&cf, sizeof(CHARFORMAT));
    14     cf.cbSize = sizeof(CHARFORMAT);
    15     cf.dwMask = CFM_COLOR;
    16     cf.crTextColor = RGB(255, 0, 0);
    17 
    18     CString strLine;
    19     for (int i = 0; i<m_richEdit.GetLineCount(); i++)
    20     {
    21         m_richEdit.GetLine(i,strLine.GetBufferSetLength(32),32);
    22         strLine.ReleaseBuffer();
    23         int nIndex = strLine.Find(_T("结束"));
    24         if(nIndex!=-1)
    25         {
    26             int lineStart = m_richEdit.LineIndex(i);//取当前行第一个字符的索引
    27             int lineEnd = lineStart + nIndex + 2;
    28             m_richEdit.SetSel(lineStart, lineEnd);//选取第一行字符
    29             m_richEdit.SetSelectionCharFormat(cf);//设置颜色
    30             
    31         }
    32     }
    33 
    34     m_richEdit.SetSel(-1,-1);
    35     m_richEdit.UpdateData(FALSE);
    36     
    37     return 0;
    38 }
    消息响应_用于更新编辑框信息

    5.OnInitDialog添加相关代码

     1 BOOL CSemaPhoreDlg::OnInitDialog()
     2 {
     3     CDialogEx::OnInitDialog();
     4 
     5     // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
     6     //  执行此操作
     7     SetIcon(m_hIcon, TRUE);            // 设置大图标
     8     SetIcon(m_hIcon, FALSE);        // 设置小图标
     9 
    10     //创建信号对象,初始和最大使用数量都为10
    11     g_hSemaPhore = CreateSemaphore(NULL,3,3,NULL);
    12     //获取主窗口句柄,供线程内部发送消息用以更新编辑框
    13     g_hwndMain = this->m_hWnd;
    14     //创建十个员工线程
    15     CloseHandle(CreateThread(NULL,0,Thread_One,NULL,0,NULL));
    16     CloseHandle(CreateThread(NULL,0,Thread_Two,NULL,0,NULL));
    17     CloseHandle(CreateThread(NULL,0,Thread_Three,NULL,0,NULL));
    18     CloseHandle(CreateThread(NULL,0,Thread_Four,NULL,0,NULL));
    19     CloseHandle(CreateThread(NULL,0,Thread_Five,NULL,0,NULL));
    20     CloseHandle(CreateThread(NULL,0,Thread_Six,NULL,0,NULL));
    21     CloseHandle(CreateThread(NULL,0,Thread_Seven,NULL,0,NULL));
    22     CloseHandle(CreateThread(NULL,0,Thread_Eight,NULL,0,NULL));
    23     CloseHandle(CreateThread(NULL,0,Thread_Nine,NULL,0,NULL));
    24     CloseHandle(CreateThread(NULL,0,Thread_Ten,NULL,0,NULL));
    25 
    26     return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
    27 }
    OnInitDialog

    6.十个员工线程函数

      1 //线程_员工1
      2 DWORD WINAPI Thread_One(LPVOID lpParam)
      3 {
      4     WaitForSingleObject(g_hSemaPhore,INFINITE);
      5 
      6     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工1:开始上网"),NULL);
      7     //沿时3秒
      8     Sleep(5000);
      9     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工1:上网结束"),NULL);
     10 
     11     //增加当前可使用计数
     12     ReleaseSemaphore(g_hSemaPhore,1,NULL);
     13 
     14     return TRUE;
     15 }
     16 //线程_员工2
     17 DWORD WINAPI Thread_Two(LPVOID lpParam)
     18 {
     19     WaitForSingleObject(g_hSemaPhore,INFINITE);
     20 
     21     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工2:开始上网"),NULL);
     22     //沿时3秒
     23     Sleep(5000);
     24     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工2:上网结束"),NULL);
     25 
     26     //增加当前可使用计数
     27     ReleaseSemaphore(g_hSemaPhore,1,NULL);
     28 
     29     return TRUE;
     30 }
     31 //线程_员工3
     32 DWORD WINAPI Thread_Three(LPVOID lpParam)
     33 {
     34     WaitForSingleObject(g_hSemaPhore,INFINITE);
     35 
     36     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工3:开始上网"),NULL);
     37     //沿时3秒
     38     Sleep(5000);
     39     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工3:上网结束"),NULL);
     40 
     41     //增加当前可使用计数
     42     ReleaseSemaphore(g_hSemaPhore,1,NULL);
     43 
     44     return TRUE;
     45 }
     46 //线程_员工4
     47 DWORD WINAPI Thread_Four(LPVOID lpParam)
     48 {
     49     WaitForSingleObject(g_hSemaPhore,INFINITE);
     50 
     51     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工4:开始上网"),NULL);
     52     //沿时3秒
     53     Sleep(5000);
     54     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工4:上网结束"),NULL);
     55 
     56     //增加当前可使用计数
     57     ReleaseSemaphore(g_hSemaPhore,1,NULL);
     58 
     59     return TRUE;
     60 }
     61 //线程_员工5
     62 DWORD WINAPI Thread_Five(LPVOID lpParam)
     63 {
     64     WaitForSingleObject(g_hSemaPhore,INFINITE);
     65 
     66     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工5:开始上网"),NULL);
     67     //沿时3秒
     68     Sleep(5000);
     69     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工5:上网结束"),NULL);
     70 
     71     //增加当前可使用计数
     72     ReleaseSemaphore(g_hSemaPhore,1,NULL);
     73 
     74     return TRUE;
     75 }
     76 //线程_员工6
     77 DWORD WINAPI Thread_Six(LPVOID lpParam)
     78 {
     79     WaitForSingleObject(g_hSemaPhore,INFINITE);
     80 
     81     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工6:开始上网"),NULL);
     82     //沿时3秒
     83     Sleep(5000);
     84     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工6:上网结束"),NULL);
     85 
     86     //增加当前可使用计数
     87     ReleaseSemaphore(g_hSemaPhore,1,NULL);
     88 
     89     return TRUE;
     90 }
     91 //线程_员工7
     92 DWORD WINAPI Thread_Seven(LPVOID lpParam)
     93 {
     94     WaitForSingleObject(g_hSemaPhore,INFINITE);
     95 
     96     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工7:开始上网"),NULL);
     97     //沿时3秒
     98     Sleep(5000);
     99     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工7:上网结束"),NULL);
    100 
    101     //增加当前可使用计数
    102     ReleaseSemaphore(g_hSemaPhore,1,NULL);
    103 
    104     return TRUE;
    105 }
    106 //线程_员工8
    107 DWORD WINAPI Thread_Eight(LPVOID lpParam)
    108 {
    109     WaitForSingleObject(g_hSemaPhore,INFINITE);
    110 
    111     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工8:开始上网"),NULL);
    112     //沿时3秒
    113     Sleep(5000);
    114     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工8:上网结束"),NULL);
    115 
    116     //增加当前可使用计数
    117     ReleaseSemaphore(g_hSemaPhore,1,NULL);
    118 
    119     return TRUE;
    120 }
    121 //线程_员工9
    122 DWORD WINAPI Thread_Nine(LPVOID lpParam)
    123 {
    124     WaitForSingleObject(g_hSemaPhore,INFINITE);
    125 
    126     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工9:开始上网"),NULL);
    127     //沿时3秒
    128     Sleep(5000);
    129     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工9:上网结束"),NULL);
    130 
    131     //增加当前可使用计数
    132     ReleaseSemaphore(g_hSemaPhore,1,NULL);
    133 
    134     return TRUE;
    135 }
    136 //线程_员工10
    137 DWORD WINAPI Thread_Ten(LPVOID lpParam)
    138 {
    139     WaitForSingleObject(g_hSemaPhore,INFINITE);
    140 
    141     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工10:开始上网"),NULL);
    142     //沿时3秒
    143     Sleep(5000);
    144     SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工10:上网结束"),NULL);
    145 
    146     //增加当前可使用计数
    147     ReleaseSemaphore(g_hSemaPhore,1,NULL);
    148 
    149     return TRUE;
    150 }
    十个员工线程函数

    7.DestroyWindow

    1 BOOL CSemaPhoreDlg::DestroyWindow()
    2 {
    3     CloseHandle(g_hSemaPhore);
    4 
    5     return CDialogEx::DestroyWindow();
    6 }
    DestroyWindow

    最终演示效果如下:

    分析下整个执行流程: 

    最大访问数量=3,  当前可访问资源数量=3

    线程_员工2:开始上网  //当前可访问资源数量=2
    线程_员工1:开始上网  //当前可访问资源数量=1
    线程_员工3:开始上网  //当前可访问资源数量=0    为0此时信号量对象为未触发状态,所有等待该信号量的线程都处于等待
    线程_员工2:上网结束  //当前可访问资源数量=1    不为0时,此时信号量对象为触发状态,等待的线程中有1个线程可以苏醒
    线程_员工3:上网结束  //当前可访问资源数量=2   此时有2个线程可以苏醒
    线程_员工1:上网结束 //当前可访问资源数量=3    此时有3个线程可以苏醒
    线程_员工4:开始上网 //当前可访问资源数量=2    此时还有2个线程可以苏醒
    线程_员工5:开始上网 //当前可访问资源数量=1    此时还有1个线程可以苏醒
    线程_员工6:开始上网 //当前可访问资源数量=0     为0此时信号量对象为未触发状态,所有等待该信号量的线程都处于等待
    线程_员工4:上网结束 //当前可访问资源数量=1    不为0时,此时信号量对象为触发状态,等待的线程中有1个线程可以苏醒
    线程_员工5:上网结束 //当前可访问资源数量=2   此时有2个线程可以苏醒
    线程_员工7:开始上网 //当前可访问资源数量=1   此时还有1个线程可以苏醒
    线程_员工6:上网结束  //当前可访问资源数量=2 此时有2个线程可以苏醒
    线程_员工8:开始上网   //当前可访问资源数量=1   此时还有1个线程可以苏醒
    线程_员工9:开始上网  //当前可访问资源数量=0     为0此时信号量对象为未触发状态,所有等待该信号量的线程都处于等待
    线程_员工7:上网结束  //当前可访问资源数量=1    不为0时,此时信号量对象为触发状态,等待的线程中有1个线程可以苏醒
    线程_员工10:开始上网 //当前可访问资源数量=0     为0此时信号量对象为未触发状态,所有等待该信号量的线程都处于等待
    线程_员工8:上网结束   //当前可访问资源数量=1    不为0时,此时信号量对象为触发状态,等待的线程中有1个线程可以苏醒
    线程_员工9:上网结束   //当前可访问资源数量=2   此时有2个线程可以苏醒
    线程_员工10:上网结束   //当前可访问资源数量=2   此时有3个线程可以苏醒

    从上面就可以看得出来,无论任何时候当前可访问资源数量都是>0 并且 小于 最大访问数量,并且始终最多只会有3条线程在运行,当有3条线程在执行时,其它线程就一直处于等待中

  • 相关阅读:
    MyISAM表锁的解决方案
    RSA数字证书管理
    Self Host WebApi服务传输层SSL加密(服务器端+客户端调用)
    WebApi服务Uri加密及验证的两种方式
    利用MVC的自定义过滤器FilterAttribute、IActionFilter、IExceptionFilter实现异常处理等功能
    html页面中meta的作用
    [转]REST简介
    [转]webApi 参数传递总结
    REST服务中的异常处理
    REST服务返回自定义的HttpResponseMessage
  • 原文地址:https://www.cnblogs.com/fzxiaoyi/p/12080089.html
Copyright © 2020-2023  润新知