多线程(四)多线程同步_Critical Section临界区
2024-08-28 21:28:14
临界区是指一个小代码段,在代码能够执行前,它必须独占对某些共享资源的访问权。和使用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
最终演示效果如下:
最新文章
- 我的第一个wcf
- iOS学习路线
- php 分页
- DRUPAL 慢的原因
- jquery.form.js表单插件的使用
- JAVA测试装饰者模式
- 将cantk runtime嵌入到现有的APP中
- DateGridView中添加下拉框列并实现数据绑定、更改背景色
- 文本读写vs二进制读写
- 自定义可判断选项是否正确listbox
- html5在手机端关于 map area中的自适应
- 自己去看dubbo源码
- 算法导论(第三版) Exercises4.2(求最大和子数组的算法优化过程)
- CF#52 C Circular RMQ (线段树区间更新)
- android保存文件到SD卡中
- T-SQL基础(7) - 透视,逆透视和分组集
- Lambda表达式和Java集合框架
- 批量压缩 css js 文件 包含多个文件 自动识别
- 描述整体程序的 app
- nginx 动态跨域配置
热门文章
- DIV 自定义滚动条样式(二)
- 剑指Offer-3.从尾到头打印链表(C++/Java)
- Paper | BLIND QUALITY ASSESSMENT OF COMPRESSED IMAGES VIA PSEUDO STRUCTURAL SIMILARITY
- Paper | Recovering Realistic Texture in Image Super-resolution by Deep Spatial Feature Transform
- FineUIPro v6.0.1 小版本更新!
- 动手学深度学习11- 多层感知机pytorch简洁实现
- MySQL 合并字段及列转行
- 利用 Javascript 让 DIV 自适应屏幕的分辨率,从而决定是否显示滚动条
- 启明星MRBS会议室预约系统V30.0发布
- VS2019已还原ReSharper的功能