博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
windows多线程同步--临界区
阅读量:7104 次
发布时间:2019-06-28

本文共 3240 字,大约阅读时间需要 10 分钟。

,一般操作系统书上面都有。

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

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

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

                                                                              

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

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#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….依次递推, 仿照文章中的代码,我们把信号量替换成临界区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#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);
}

 

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

本文转自tenos博客园博客,原文链接:http://www.cnblogs.com/TenosDoIt/p/3601308.html,如需转载请自行联系原作者
你可能感兴趣的文章
-[TTRequestLoader connection:didReceiveResponse...
查看>>
程序Debug运行的时候,老是显示“0x755c9617 处最可能的异常: 0x000006B...
查看>>
apkplug主题切换功能之主题包打包编译-07
查看>>
手把手让你实现开源企业级web高并发解决方案
查看>>
circular buffer in Linux kernel
查看>>
Bug描述
查看>>
mac下的项目管理软件OmniPlan的使用
查看>>
iOS为网站添加图标到主屏幕以及增加启动画面
查看>>
matlab的一些用发plot颜色
查看>>
生存的现状及环境
查看>>
线程入门-使用Execcutor
查看>>
setNeedsLayout 与 setNeedsDisplay
查看>>
康托展开与逆康托展开
查看>>
hadoop 2.7.2 安装
查看>>
JAVA泛型详解——转
查看>>
#ifdef __cplusplus extern "C" { #endif 的解释<转>
查看>>
高速队类实现(线程安全)
查看>>
css3 transition 过渡动画
查看>>
CSS基础入门视频教程荟萃
查看>>
centos7 安装maven
查看>>