临界区是指一个小代码段,在代码能够执行前,它必须独占对某些共享资源的访问权。和使用mutex一样,它们都是以原子操作方式来对共享资源进行访问。

临界区又叫关键代码段,与上一篇的mutex互斥体实现的功能一样,都是为了让多线程同步

从上面图片可以看到二者的区别,如果是在当前进程进行线程同步,只需要采用临界区即可

如果需要跨进程,就采用互斥体,最常用的一种情况就是通过在程序初始化时先打开斥体对象,如果失败就创建一个互斥体对象,反之就结束进程,以此来保证程序在任意时间只运行一份

临界区中所有API都需要传递一个C R I T I C A L _ S E C T I O N结构指针,这个结构不需要对其中成员进行赋值,只需要定义。当使用相关API时,传递结构地址即可

注意:在进入临界区和退出临界区之间的代码都处于加锁受保护状态,千万不要在其间写sleep之类的函数,否则可能出现死锁

初始化临界区
VOID InitializeCriticalSection(PCRITICAL_SECTION pcs);
1. C R I T I C A L _ S E C T I O N结构指针
该函数用于对结构的各个成员进行初始化. 注意:当不使用临界区时,需要删除临界区

删除临界区
VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);
1. C R I T I C A L _ S E C T I O N结构指针
该函数用于对结构中的成员变量进行删除做清理工作。如果有任何线程仍然使用关键代码段,那么不应该删除该代码段

进入临界区
VOID EnterCriticalSection(PCRITICAL_SECTION pcs);
1. C R I T I C A L _ S E C T I O N结构指针
这个函数功能与WaitForSingleObject类相似,它会检测CRITICAL_SECTION中成员根据不种情况做不同操作

1. 如果没有线程拥有访问权,就会修改成员使之指向当前线程,访问计数为1,使当前线程拥有对CRITICAL_SECTION的所有权并且立即返回
2. 如果拥有访问权的是当前线程,就会把访问计数+1. 如果出现这种情况,说明当前线程在某一时刻进入了临界区,但是没有退出临界区,又重新进入了临界区,明显是个错误的操作
3.如果拥有访问权的是其它线程,就会进入等待状态,直到得到拥有权变有可调度状态才会苏醒

注意:当线程进入临界区,不再访问共享资源时,需要退出临界区,否则将出现第2种错误的操作发生

退出离界区
VOID LeaveCriticalSection(PCRITICAL_SECTION pcs);
1.PCRITICAL_SECTION

它会检测CRITICAL_SECTION中成员,如果为0,将不做任何操作直接返回. 如果计数大于0,就-1,如果结果为0,查找等待的线程数量是否大于0?
如果大于1,就从所有等待线中挑选一个线程成为新的拥有者,修改计数器为1,然后返回. 反之如果等待线程数量为0,直接返回

编写一个Demo用于演示Critical Section临界区基本操作,功能与上一篇相同

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

2. 添加一个编辑框用于显示售票信息,修改ID为IDC_EDIT_SHOWINFO, 修改属性为不可读.

3.添加一个按钮用于启动线程,修改ID为IDC_BTN_SELLTICKET

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

 int g_nTickNum = ; //总票数
CEdit* g_editShowInfo; //编辑框控件指针
CRITICAL_SECTION g_cs; //临界区对象 //线程函数前置声明
DWORD WINAPI ThreadSellTicket_One(LPVOID lpParam);
DWORD WINAPI ThreadSellTicket_Two(LPVOID lpParam);
DWORD WINAPI ThreadSellTicket_Three(LPVOID lpParam);

全局变量和线程函数前置声明

5.OnInitDialog中添加相应代码

 BOOL CCriticalSectionDemoDlg::OnInitDialog()
{
CDialogEx::OnInitDialog(); // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标 //初始化临界区
InitializeCriticalSection(&g_cs);
//获取EDIT控件指针,供线程内部访问
g_editShowInfo = (CEdit*)GetDlgItem(IDC_EDIT_SHOWINFO); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}

OnInitDialog

6.按钮_启动线程事件

 void CCriticalSectionDemoDlg::OnBnClickedBtnSellticket()
{
//创建三个售票窗口线程,创建后直接关闭句柄
CloseHandle(CreateThread(NULL,,ThreadSellTicket_One,NULL,,NULL));
CloseHandle(CreateThread(NULL,,ThreadSellTicket_Two,NULL,,NULL));
CloseHandle(CreateThread(NULL,,ThreadSellTicket_Three,NULL,,NULL));
}

按钮_启动线程事件

7.三个售票窗口线程代码

 //线程_售票窗口1
DWORD WINAPI ThreadSellTicket_One(LPVOID lpParam)
{
CString strEdit;
CString strNew;
while (true)
{
Sleep();
EnterCriticalSection(&g_cs);
g_editShowInfo->GetWindowText(strEdit);
if(g_nTickNum > )
{
strNew.Format(_T("线程1:剩余票数:%d,售出1张票\r\n"),g_nTickNum--);
strEdit += strNew;
if(g_nTickNum == )
{
strEdit += _T("线程1:票己售空,关闭售票窗口\r\n");
g_editShowInfo->SetWindowText(strEdit);
LeaveCriticalSection(&g_cs);
break;
}
else
{
g_editShowInfo->SetWindowText(strEdit);
LeaveCriticalSection(&g_cs);
}
}
else
{
strEdit += _T("线程1:票己售空,关闭售票窗口\r\n");
g_editShowInfo->SetWindowTextW(strEdit);
LeaveCriticalSection(&g_cs);
break;
}
}
return true;
}

线程_售票窗口1

 DWORD WINAPI ThreadSellTicket_Two(LPVOID lpParam)
{
CString strEdit;
CString strNew;
while (true)
{
Sleep();
EnterCriticalSection(&g_cs);
g_editShowInfo->GetWindowText(strEdit);
if(g_nTickNum > )
{
strNew.Format(_T("线程2:剩余票数:%d,售出1张票\r\n"),g_nTickNum--);
strEdit += strNew;
if(g_nTickNum == )
{
strEdit += _T("线程2:票己售空,关闭售票窗口\r\n");
g_editShowInfo->SetWindowText(strEdit);
LeaveCriticalSection(&g_cs);
break;
}
else
{
g_editShowInfo->SetWindowText(strEdit);
LeaveCriticalSection(&g_cs);
}
}
else
{
strEdit += _T("线程2:票己售空,关闭售票窗口\r\n");
g_editShowInfo->SetWindowTextW(strEdit);
LeaveCriticalSection(&g_cs);
break;
}
}
return true;
}

线程_售票窗口2

 DWORD WINAPI ThreadSellTicket_Three(LPVOID lpParam)
{
CString strEdit;
CString strNew;
while (true)
{
Sleep();
EnterCriticalSection(&g_cs);
g_editShowInfo->GetWindowText(strEdit);
if(g_nTickNum > )
{
strNew.Format(_T("线程3:剩余票数:%d,售出1张票\r\n"),g_nTickNum--);
strEdit += strNew;
if(g_nTickNum == )
{
strEdit += _T("线程3:票己售空,关闭售票窗口\r\n");
g_editShowInfo->SetWindowText(strEdit);
LeaveCriticalSection(&g_cs);
break;
}
else
{
g_editShowInfo->SetWindowText(strEdit);
LeaveCriticalSection(&g_cs);
}
}
else
{
strEdit += _T("线程3:票己售空,关闭售票窗口\r\n");
g_editShowInfo->SetWindowTextW(strEdit);
LeaveCriticalSection(&g_cs);
break;
}
}
return true;
}

线程_售票窗口3

8.DestroyWindow添加相应代码

 BOOL CCriticalSectionDemoDlg::DestroyWindow()
{
//删除临界区
DeleteCriticalSection(&g_cs);
return CDialogEx::DestroyWindow();
}

DestroyWindow

最终演示效果如下:

最新文章

  1. 我的第一个wcf
  2. iOS学习路线
  3. php 分页
  4. DRUPAL 慢的原因
  5. jquery.form.js表单插件的使用
  6. JAVA测试装饰者模式
  7. 将cantk runtime嵌入到现有的APP中
  8. DateGridView中添加下拉框列并实现数据绑定、更改背景色
  9. 文本读写vs二进制读写
  10. 自定义可判断选项是否正确listbox
  11. html5在手机端关于 map area中的自适应
  12. 自己去看dubbo源码
  13. 算法导论(第三版) Exercises4.2(求最大和子数组的算法优化过程)
  14. CF#52 C Circular RMQ (线段树区间更新)
  15. android保存文件到SD卡中
  16. T-SQL基础(7) - 透视,逆透视和分组集
  17. Lambda表达式和Java集合框架
  18. 批量压缩 css js 文件 包含多个文件 自动识别
  19. 描述整体程序的 app
  20. nginx 动态跨域配置

热门文章

  1. DIV 自定义滚动条样式(二)
  2. 剑指Offer-3.从尾到头打印链表(C++/Java)
  3. Paper | BLIND QUALITY ASSESSMENT OF COMPRESSED IMAGES VIA PSEUDO STRUCTURAL SIMILARITY
  4. Paper | Recovering Realistic Texture in Image Super-resolution by Deep Spatial Feature Transform
  5. FineUIPro v6.0.1 小版本更新!
  6. 动手学深度学习11- 多层感知机pytorch简洁实现
  7. MySQL 合并字段及列转行
  8. 利用 Javascript 让 DIV 自适应屏幕的分辨率,从而决定是否显示滚动条
  9. 启明星MRBS会议室预约系统V30.0发布
  10. VS2019已还原ReSharper的功能