临界区,互斥量,信号量,事件的区别
临界区(Critical section)与互斥体(Mutex)的区别
1、临界区只能用于对象在同一进程里线程间的互斥访问;互斥体可以用于对象进程间或线程间的互斥访问。
2、临界区是非内核对象,只在用户态进行锁操作,速度快;互斥体是内核对象,在核心态进行锁操作,速度慢。
3、临界区和互斥体在Windows平台都下可用;Linux下只有互斥体可用。
临界区,互斥量,信号量,事件的区别
http://blog.csdn.net/wuyuan2011woaini/article/details/7564825
事件对象:
事件对象也属于内核对象,包含一个计数器,一个用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件是处于已通知还是未通知状态的布尔值。
关键代码段(临界区)是指一小代码段,在代码能够执行前,它必须独占对某些资源的访问权。
线程死锁:
线程1拥有了临界区对象A,等待临界区对象B的拥有权,线程2拥有了临界区对象B,等待临界区对象A的拥有权,就造成了死锁。
互斥对象、事件对象和关键代码段的比较
互斥对象和事件对象都属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。
关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。
VC下线程同步的三种方法(互斥、事件、临界区)
首选使用临界区对象,主要原因是使用简单。
EnterCriticalSection()函数等候指定的危险区段对象的所有权。当调用的线程被允许所有权时,函数返回。
EnterCriticalSection (),一个单独进程的线程可以使用一个危险区段对象作为相互-排除同步。 进程负责分配被一个危险区段对象使用的内存, 它藉由声明一个CRITICAL_SECTION类型 的变量实现。在使用一个危险区段之前,进程的一些线程必须调用 InitializeCriticalSection 函数设定对象的初值.
为了要使互斥的访问被共享的资源,每个线程调用EnterCriticalSection 或者 TryEnterCriticalSection 功能,在执行访问被保护资源的任何代码段之前,请求危险区段的所有权。
01
|
#include <windows.h>
|
02
|
#include <iostream>
|
03
|
using namespace std;
|
04
|
DWORD WINAPI Fun1Proc( LPVOID lpParameter);
|
05
|
DWORD WINAPI Fun2Proc( LPVOID lpParameter);
|
06
|
int tickets=100;
|
07
|
CRITICAL_SECTION g_csA;
|
08
|
CRITICAL_SECTION g_csB;
|
09
|
void main()
|
10
|
{
|
11
|
HANDLE hThread1;
|
12
|
HANDLE hThread2;
|
13
|
hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
|
14
|
hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
|
15
|
CloseHandle(hThread1);
|
16
|
CloseHandle(hThread2);
|
17
|
InitializeCriticalSection(&g_csA);
|
18
|
InitializeCriticalSection(&g_csB);
|
19
|
Sleep(40000);
|
20
|
DeleteCriticalSection(&g_csA);
|
21
|
DeleteCriticalSection(&g_csB);
|
22
|
}
|
23
|
DWORD WINAPI Fun1Proc( LPVOID lpParameter)
|
24
|
{
|
25
|
while (TRUE)
|
26
|
{
|
27
|
EnterCriticalSection(&g_csA);
|
28
|
Sleep(1);
|
29
|
//EnterCriticalSection(&g_csB);//临界区的同步和互锁
|
30
|
if (tickets>0)
|
31
|
{
|
32
|
Sleep(1);
|
33
|
cout<< "Thread1 sell ticket :" <<tickets--<<endl;
|
34
|
//LeaveCriticalSection(&g_csB);
|
35
|
LeaveCriticalSection(&g_csA);
|
36
|
}
|
37
|
else
|
38
|
{
|
39
|
//LeaveCriticalSection(&g_csB);
|
40
|
LeaveCriticalSection(&g_csA);
|
41
|
break ;
|
42
|
}
|
43
|
}
|
44
|
return 0;
|
45
|
}
|
46
|
DWORD WINAPI Fun2Proc( LPVOID lpParameter)
|
47
|
{
|
48
|
while (TRUE)
|
49
|
{
|
50
|
EnterCriticalSection(&g_csB);
|
51
|
Sleep(1);
|
52
|
EnterCriticalSection(&g_csA);
|
53
|
if (tickets>0)
|
54
|
{
|
55
|
Sleep(1);
|
56
|
cout<< "Thread2 sell ticket :" <<tickets--<<endl;
|
57
|
LeaveCriticalSection(&g_csA);
|
58
|
LeaveCriticalSection(&g_csB);
|
59
|
}
|
60
|
else
|
61
|
{
|
62
|
LeaveCriticalSection(&g_csA);
|
63
|
LeaveCriticalSection(&g_csB);
|
64
|
break ;
|
65
|
}
|
66
|
}
|
67
|
return 0;
|
68
|
}
|
--------------------------------------------------------------------------------
二、使用互斥对象
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
如果时间是有信号状态返回WAIT_OBJECT_0,如果时间超过dwMilliseconds值但时间事件还是无信号状态则返回WAIT_TIMEOUT
WaitForSingleObject函数用来检测hHandle事件的信号状态,当函数的执行时间超过dwMilliseconds就返回,但如果参数dwMilliseconds为INFINITE时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到WaitForSingleObject有返回直才执行后面的代码。
01
|
#include <windows.h>
|
02
|
#include <iostream>
|
03
|
using namespace std;
|
04
|
DWORD WINAPI Fun1Proc( LPVOID lpParameter);
|
05
|
DWORD WINAPI Fun2Proc( LPVOID lpParameter);
|
06
|
int index =0;
|
07
|
int tickets=100;
|
08
|
HANDLE hMutex;
|
09
|
void main()
|
10
|
{
|
11
|
HANDLE hThread1;
|
12
|
HANDLE hThread2;
|
13
|
//创建线程
|
14
|
hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
|
15
|
hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
|
16
|
CloseHandle(hThread1);
|
17
|
CloseHandle(hThread2);
|
18
|
//**************************************************************
|
19
|
//保证应用程序只有一个实例运行,创建一个命名的互斥对象.
|
20
|
hMutex=CreateMutex(NULL,TRUE, LPCTSTR ( "tickets" ));
|
21
|
//创建时主线程拥有该互斥对象,互斥对象的线程ID为主线程的ID,同时将该互斥对象内部计数器置为1
|
22
|
if (hMutex)
|
23
|
{
|
24
|
if (ERROR_ALREADY_EXISTS==GetLastError())
|
25
|
{
|
26
|
cout<< "only one instance can run!" <<endl;
|
27
|
//Sleep(40000);
|
28
|
return ;
|
29
|
}
|
30
|
}
|
31
|
//**************************************************************
|
32
|
WaitForSingleObject(hMutex,INFINITE);
|
33
|
//使用该函数请求互斥对象时,虽说该对象处于无信号状态,但因为请求的线程ID和该互斥对象所有者的线程ID是相同的.所以仍然可以请求到这个互斥对象,于是该互斥对象内部计数器加1,内部计数器的值为2. 意思是有两个等待动作
|
34
|
ReleaseMutex(hMutex); //释放一次互斥对象,该互斥对象内部计数器的值递减1,操作系统不会将这个互斥对象变为已通知状态.
|
35
|
ReleaseMutex(hMutex); //释放一次互斥对象,该互斥对象内部计数器的值为0,同时将该对象设置为已通知状态.
|
36
|
//对于互斥对象来说,谁拥有谁释放
|
37
|
Sleep(40000);
|
38
|
}
|
39
|
DWORD WINAPI Fun1Proc( LPVOID lpParameter)
|
40
|
{
|
41
|
while (TRUE)
|
42
|
{
|
43
|
WaitForSingleObject(hMutex,INFINITE); //等待互斥对象有信号
|
44
|
if (tickets>0)
|
45
|
{
|
46
|
Sleep(1);
|
47
|
cout<< "thread1 sell ticket :" <<tickets--<<endl;
|
48
|
}
|
49
|
else
|
50
|
break ;
|
51
|
ReleaseMutex(hMutex); //设置该互斥对象的线程ID为0,并且将该对象设置为有信号状态
|
52
|
}
|
53
|
return 0;
|
54
|
}
|
55
|
DWORD WINAPI Fun2Proc( LPVOID lpParameter)
|
56
|
{
|
57
|
while (TRUE)
|
58
|
{
|
59
|
WaitForSingleObject(hMutex,INFINITE);
|
60
|
if (tickets>0)
|
61
|
{
|
62
|
Sleep(1);
|
63
|
cout<< "thread2 sell ticket :" <<tickets--<<endl;
|
64
|
}
|
65
|
else
|
66
|
break ;
|
67
|
ReleaseMutex(hMutex);
|
68
|
}
|
69
|
return 0;
|
70
|
}
|
--------------------------------------------------------------------------------
三、使用事件对象
1
|
HANDLE CreateEvent(
|
2
|
LPSECURITY_ATTRIBUTES lpEventAttributes, // SD
|
3
|
BOOL bManualReset, // reset type
|
4
|
BOOL bInitialState, // initial state
|
5
|
LPCTSTR lpName // object name
|
6
|
);
|
该函数创建一个Event同步对象,并返回该对象的Handle
lpEventAttributes 一般为NULL
bManualReset 创建的Event是自动复位还是人工复位 ,如果true,人工复位,
一旦该Event被设置为有信号,则它一直会等到ResetEvent()API被调用时才会恢复
为无信号. 如果为false,Event被设置为有信号,则当有一个wait到它的Thread时,
该Event就会自动复位,变成无信号.
bInitialState 初始状态,true,有信号,false无信号
lpName Event对象名
一个Event被创建以后,可以用OpenEvent()API来获得它的Handle,用CloseHandle()
来关闭它,用SetEvent()或PulseEvent()来设置它使其有信号,用ResetEvent()
来使其无信号,用WaitForSingleObject()或WaitForMultipleObjects()来等待
其变为有信号.
PulseEvent()是一个比较有意思的使用方法,正如这个API的名字,它使一个Event
对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的.
对自动复位的Event对象,它仅释放第一个等到该事件的thread(如果有),而对于
人工复位的Event对象,它释放所有等待的thread.
显示代码 打印
01
|
#include <windows.h>
|
02
|
#include <iostream>
|
03
|
using namespace std;
|
04
|
DWORD WINAPI Fun1Proc( LPVOID lpParameter);
|
05
|
DWORD WINAPI Fun2Proc( LPVOID lpParameter);
|
06
|
int tickets=100;
|
07
|
HANDLE g_hEvent;
|
08
|
void main()
|
09
|
{
|
10
|
HANDLE hThread1;
|
11
|
HANDLE hThread2;
|
12
|
//**************************************************
|
13
|
//创建一个命名的自动重置事件内核对象
|
14
|
g_hEvent=CreateEvent(NULL,FALSE,FALSE, LPCTSTR ( "tickets" ));
|
15
|
if (g_hEvent)
|
16
|
{
|
17
|
if (ERROR_ALREADY_EXISTS==GetLastError())
|
18
|
{
|
19
|
cout<< "only one instance can run!" <<endl;
|
20
|
return ;
|
21
|
}
|
22
|
}
|
23
|
//**************************************************
|
24
|
SetEvent(g_hEvent);
|
25
|
//创建线程
|
26
|
hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
|
27
|
hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
|
28
|
Sleep(40000);
|
29
|
//关闭事件对象句柄
|
30
|
CloseHandle(g_hEvent);
|
31
|
}
|
32
|
DWORD WINAPI Fun1Proc( LPVOID lpParameter)
|
33
|
{
|
34
|
while (TRUE)
|
35
|
{
|
36
|
WaitForSingleObject(g_hEvent,INFINITE);
|
37
|
//ResetEvent(g_hEvent);
|
38
|
if (tickets>0)
|
39
|
{
|
40
|
Sleep(1);
|
41
|
cout<< "thread1 sell ticket :" <<tickets--<<endl;
|
42
|
SetEvent(g_hEvent);
|
43
|
}
|
44
|
else
|
45
|
{
|
46
|
SetEvent(g_hEvent);
|
47
|
break ;
|
48
|
}
|
49
|
}
|
50
|
return 0;
|
51
|
}
|
52
|
DWORD WINAPI Fun2Proc( LPVOID lpParameter)
|
53
|
{
|
54
|
while (TRUE)
|
55
|
{
|
56
|
WaitForSingleObject(g_hEvent,INFINITE);
|
57
|
//ResetEvent(g_hEvent);
|
58
|
if (tickets>0)
|
59
|
{
|
60
|
cout<< "Thread2 sell ticket :" <<tickets--<<endl;
|
61
|
SetEvent(g_hEvent);
|
62
|
}
|
63
|
else
|
64
|
{
|
65
|
SetEvent(g_hEvent);
|
66
|
break ;
|
67
|
}
|
68
|
}
|
69
|
return 0;
|
转自:http://blog.csdn.net/wuyuan2011woaini/article/details/7564842
临界区,互斥量,信号量,事件的区别相关推荐
- 线程同步(临界区、互斥量、事件、信号量)
1.为什么线程要同步? #include<windows.h> #include<iostream> using namespace std; DWORD WINAPI Thr ...
- 秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量
前面<秒杀多线程第四篇一个经典的多线程同步问题>提出了一个经典的多线程同步互斥问题,这个问题包括了主线程与子线程的同步,子线程间的互斥,是一道非常经典的多线程同步互斥问题范例,后面分别用了 ...
- 任务间同步 | 信号量、互斥量和事件集
本文分享自中移OneOS公众号<任务间同步>. 多个任务操作同一块代码区域,这块代码就称为临界区,如果任何时刻最多只允许一个任务去使用临界区,那么多个任务就需要互斥的访问.当一个任务占用此 ...
- 秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题
秒杀多线程第十五篇 关键段,事件,互斥量,信号量的"遗弃"问题 在<秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量>中对经典多线程同步互斥问题进行了回 ...
- 临界区、互斥量、事件、信号量四种方式
临界区(Critical Section).互斥量(Mutex).信号量(Semaphore).事件(Event)的区别 1.临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据 ...
- win32下多线程同步方式之临界区,互斥量,事件对象,信号量
// win32_thread_syn.cpp : 定义控制台应用程序的入口点. //#include "stdafx.h" #include "iostream&quo ...
- 经典线程同步总结 关键段 事件 互斥量 信号量
本文参考了http://blog.csdn.net/morewindows/article/details/7538247 1.线程(进程)同步的主要任务 答:在引入多线程后,由于线程执行的异步性,会 ...
- RT-Thread 线程同步及通信 -- 信号量、互斥量、事件、邮箱、消息队列
目录 一 RT-Thread 信号量 二 RT-Thread 互斥量 三 RT-Thread 事件标志组 四 RT-Thread 邮箱 五 RT-Thread 消息队列 一 RT-Thre ...
- C++多线程 互斥锁 信号量 事件 临界区
一.互斥锁 1.先熟悉熟悉API 1,创建互斥锁,并反正一个句柄 HANDLE CreateMutex( LPSECURITY_ATTRIBUTESlpMutexAttributes, // 指向安全 ...
- 信号量 互斥量 条件变量
原文:https://blog.csdn.net/qq_32646795/article/details/78221005 本文打算写一些和锁有关的东西,谈一谈我对锁的原理和实现的理解,主要包含以下方 ...
最新文章
- Android Studio简单设置
- python打开excel表格-如何从python中用excel打开excel工作表?
- 【CTF】paradigm-CTF babysandbox
- harbor登录验证_Harbor 源码浅析
- 读书印记 - 《沟通的艺术:一本手把手教你社交沟通的书》
- Ubuntu配置完全教程
- arm linux 加载.a,如何将libc.a链接到arm-linux中的共享库中使用arm-none-linux-gnueabi-gcc...
- java的super_Java中this和super的用法总结
- 协议编码分析 - ARP协议详解
- 设计模式 - 状态模式、职责连模式
- 简单爬虫 爬取百度图片并批量重命名
- PPT 设置幻灯片母版
- Java零基础学习-每日单词(日更)
- ubuntu 19查看和修改时区
- 【Blender报错记录】Bone Heat Weighting: failed to find solution for one or more bones
- TCP快速恢复算法PRR
- thread.java 619_java jstack thread 映射 linux 线程(LWP) | 学步园
- 在微信中怎么打开微信运动?福利干货!如何在微信中开启微信运动记录每天运动步数?
- 【札记】Python处理TSV文件以及144790个英语单词的注音、释义、例句的.sql和.tsv文件下载
- 什么是期权股权?什么是期权股权原始股?
热门文章
- malloc开辟的空间在哪一个区间_专业指南 | 室内设计和空间设计区别,到底该选哪一个?...
- 马斯克谈买比特币:当法币实际利率为负时 只有“傻子”才不放眼他处
- SecureCRT终端仿真程序下载及安装使用
- 信安教程第二版-第12章网络安全审计技术原理与应用
- Java基础---面向对象(OOP)
- 你准备好了吗,江湖来了
- 计算机网络基础-目录
- python3 enumerate()函数笔记
- 微博关注者数量在计算中的作用
- MySQL 我们来聊聊 count(*) 语句的优化方式有哪些?