initializecriticalsection(initializecriticalsection头文件)满满干货
为什么要进行线程同步在多线程的程序中,很少有多个线程能在其生命期内进行完全独立的操作;通常情况是一些线程进行某些操作,而其他的线程必须对其操作后
为什么要进行线程同步在多线程的程序中,很少有多个线程能在其生命期内进行完全独立的操作;通常情况是一些线程进行某些操作,而其他的线程必须对其操作后的结果进行了解如果不采取同步机制,其他线程会在线程处理任务前访问处理结果,这样会产生错误的了解。
例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题;若一个线程负责写操作,其他线程负责读取操作,则不能保证读取的就是修改过的值,这时就必须在变量写操作过程时加上访问限制,在写操作完成后解除访问限制。
这种保证线程能正确获取其他线程处理结束后的结果的措施称为线程同步线程同步的四种方式:同步方式速度、资源开销跨进程资源统计Critical Section速度快、非内核对象不能用于不同进程不能资源统计(每次只能有一个线程对共享资源进行存取)
Mutex速度慢,内核对象可用于不同进程不能资源统计Semaphore速度慢、内核对象可用于不同进程可资源统计(可以让一个或多个线程对共享资源进行存取)Event速度慢、内核对象可用于不同进程可资源统计
Critical Section临界区(Critical Section):通过对多线程的串行化来访问公共资源或一段代码,本身不是内核对象,速度快,适合控制数据访问在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开。
临界区被释放后,其他线程才可以抢占【初始化临界区】VOIDWINAPI InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);【删除临界区】
VOIDWINAPI DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);【获取临界区】VOIDWINAPI EnterCriticalSection(
LPCRITICAL_SECTIONlpCriticalSection);【释放临界区】VOIDWINAPI LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection
);临界区在使用时,以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()和LeaveCriticalSection()函数占有和释放一个临界区所用到的。
CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。
【示例】#include#include#include#include#includeint counter_value =
0;/*counter value*/int max_counter = 5;/*counter value max*/int min_counter = 0;/*counter value min*/
int producer_num = 0;/*生产者进入临界区次数*/int consumer_num = 0;/*消费者进入临界区次数*/ CRITICAL_SECTION critical_section;
/*临界区*/DWORD WINAPI producer(LPVOID param){ int* id = (int*)param; while (true) { Sleep(rand() %
1000); srand(counter_value); EnterCriticalSection(&critical_section); int add_count = rand() %
6 + 1; /*判断是否超过最大值*/if (counter_value < max_counter) { counter_value += add_count;
if (counter_value > max_counter) { add_count -= counter_value - max_counter; counter_value = max_counter; }
printf("Producer%d : produced %d items\n", *id, add_count); } else { printf("Producer%d : counter value is full, cancel producing...\n"
, *id); } printf("items num is %d\n", counter_value); LeaveCriticalSection(&critical_section);
//生产者进入临界区,次数增加 producer_num++; } } DWORD WINAPI consumer(LPVOID param){ int* id = (int*)param;
while (true) { //sleep for a random period of time Sleep(rand() % 1000); EnterCriticalSection(&critical_section);
// generate srand(counter_value); int decrease_count = rand() % 6 + 1; //判断是否超过最小值if (counter_value > min_counter) { counter_value -= decrease_count;
if (counter_value <= min_counter) { decrease_count -= min_counter - counter_value; counter_value = min_counter; }
printf("Consumer%d : consumed %d items\n", *id, decrease_count); } else { printf("Consumer%d : counter value is less than mixinum, cancel consuming...\n"
, *id); } printf("items num is %d\n", counter_value); LeaveCriticalSection(&critical_section);
//消费者进入临界区,次数增加 consumer_num++; } return0; } intmain(){ srand(counter_value); int thread_producer =
5; int thread_consumer = 5; int pvalue[5] = { 0 };/*生产者*/int cvalue[5] = { 0 };/*消费者*/ DWORD thread_pid[
5], thread_cid[5]; HANDLE hthread_p[5], hthread_c[5];/*生产者和消费者线程*/ InitializeCriticalSection(&critical_section);
//FILE* fp;//freopen_s(&fp, "CriticalSection_output.txt", "w", stdout);/*create producer thread*/for (
int i = 0; i < thread_producer; ++i) { pvalue[i] = i + 1; hthread_p[i] = CreateThread(NULL,
0, producer, &pvalue[i], 0, &thread_pid[i]); } /*create consumer thread*/for (int i = 0; i < thread_consumer; ++i) { cvalue[i] = i +
1; hthread_c[i] = CreateThread(NULL, 0, consumer, &cvalue[i], 0, &thread_cid[i]); } Sleep(1000
); for (int i = 0; i < thread_producer; ++i) { WaitForSingleObject(hthread_p[i], INFINITE); }
for (int i = 0; i < thread_producer; ++i) { WaitForSingleObject(hthread_c[i], INFINITE); } DeleteCriticalSection(&critical_section);
//fclose(stdout);return0; }Mutex互斥量(Mutex):只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。
【创建互斥量】HANDLE WINAPI CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes,// pointer to security attributes
BOOL bInitialOwner, // flag for initial ownership LPCTSTR lpName // pointer to mutex-object name
); //参数意义://lpMutextAttributes 传递安全相关的配置信息,使用默认安全设置时可以传递NULL//bInitialOwner 如果为TRUE,则创建出的互斥量对象属于调用该函数的线程,同时进入non-signaled状态;
//如果为FALSE,则创建出的互斥量对象不属于任何线程,此时状态为signaled//lpName 用于命名互斥量对象传入NULL时创建无名的互斥量对象【销毁互斥量】BOOLWINAPI CloseHandle(。
HANDLEhObject);【获取互斥量】//获取函数 Windows线程创建中介绍的此函数,用于针对单个内核对象验证signaledDWORD WINAPI WaitForSingleObject。
( HANDLE hHandle, // handle to object to wait for DWORD dwMilliseconds // time-out interval in milliseconds
);【释放互斥量】BOOL WINAPI ReleaseMutex( HANDLE hMutex //需要释放的对象的句柄 );互斥量被某一线程获取时为non-signaled状态,释放时进入
signaled状态因此,可以利用WaitForSingleObject函数验证互斥量是否已分配互斥量在WaitForSingleObject函数返回时自动进入non-signaled状态,因为它是“auto-reset
”模式的内核对象【示例】#include#include#include#include#includeint counter_value =
0;/*counter value*/int max_counter = 5;/*counter value max*/int min_counter = 0;/*counter value min*/
int producer_num = 0;/*生产者进入临界区次数*/int consumer_num = 0;/*消费者进入临界区次数*/ HANDLE Mutex = NULL;/*互斥锁*/DWORD WINAPI
producer(LPVOID param){ int* id = (int*)param; while (true) { Sleep(rand() % 1000); srand(counter_value); WaitForSingleObject(Mutex, INFINITE);
int add_count = rand() % 6 + 1; /*判断是否超过最大值*/if (counter_value < max_counter) { counter_value += add_count;
if (counter_value > max_counter) { add_count -= counter_value - max_counter; counter_value = max_counter; }
printf("Producer%d : produced %d items\n", *id, add_count); } else { printf("Producer%d : counter value is full, cancel producing...\n"
, *id); } printf("items num is %d\n", counter_value); ReleaseMutex(Mutex); //生产者进入临界区,次数增加
producer_num++; } } DWORD WINAPI consumer(LPVOID param){ int* id = (int*)param; while (true
) { //sleep for a random period of time Sleep(rand() % 1000); WaitForSingleObject(Mutex, INFINITE);
// generate srand(counter_value); int decrease_count = rand() % 6 + 1; //判断是否超过最小值if (counter_value > min_counter) { counter_value -= decrease_count;
if (counter_value <= min_counter) { decrease_count -= min_counter - counter_value; counter_value = min_counter; }
printf("Consumer%d : consumed %d items\n", *id, decrease_count); } else { printf("Consumer%d : counter value is less than mixinum, cancel consuming...\n"
, *id); } printf("items num is %d\n", counter_value); ReleaseMutex(Mutex); //消费者进入临界区,次数增加
consumer_num++; } return0; } intmain(){ srand(counter_value); int thread_producer = 5;
int thread_consumer = 5; int pvalue[5] = { 0 };/*生产者*/int cvalue[5] = { 0 };/*消费者*/ DWORD thread_pid[
5], thread_cid[5]; HANDLE hthread_p[5], hthread_c[5];/*生产者和消费者线程*/ Mutex = CreateMutex(NULL, FALSE,
NULL); //FILE* fp;//freopen_s(&fp, "CriticalSection_output.txt", "w", stdout);/*create producer thread*/
for (int i = 0; i < thread_producer; ++i) { pvalue[i] = i + 1; hthread_p[i] = CreateThread(
NULL, 0, producer, &pvalue[i], 0, &thread_pid[i]); } /*create consumer thread*/for (int i = 0; i < thread_consumer; ++i) { cvalue[i] = i +
1; hthread_c[i] = CreateThread(NULL, 0, consumer, &cvalue[i], 0, &thread_cid[i]); } Sleep(1000
); for (int i = 0; i < thread_producer; ++i) { WaitForSingleObject(hthread_p[i], INFINITE); }
for (int i = 0; i < thread_producer; ++i) { WaitForSingleObject(hthread_c[i], INFINITE); } CloseHandle(Mutex);
//fclose(stdout);return0; }Semaphore信号量(Semaphore)是维护0到指定最大值之间的同步对象信号量状态在其计数大于0时是有信号,而其计数是0时是无信号的信号量对象在控制上可以支持有限数量共享资源的访问。
信号量的特点和用途可用下列几句话定义:如果当前资源的数量大于0,则信号量有效;如果当前资源数量是0,则信号量无效;系统决不允许当前资源的数量为负值;当前资源数量决不能大于最大资源数量【创建信号量】HANDLE WINAPI 。
CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //信号量的安全属性 LONG lInitialCount,//开始时可供使用的资源数
LONG lMaximumCount,//最大资源数 LPCWSTR lpName //信号量的名称 );【释放信号量】BOOL WINAPI ReleaseSemaphore( HANDLE hSemaphore,
//要增加的信号量句柄 LONG lReleaseCount,//信号量的当前资源数增加lReleaseCount LPLONG lpPreviousCount //增加前的数值返回 );【打开信号量】
HANDLE WINAPI OpenSemaphore( DWORD dwDesiredAccess, //access BOOL bInheritHandle,//如果允许子进程继承句柄,则设为TRUE
LPCWSTR lpName //指定要打开的对象的名字 );【销毁信号量】BOOL WINAPI CloseHandle(HANDLE hObject);【示例】#include
#include#include#include#includeint semaphore_num = 1; /*定义全局变量*/ HANDLE hSemaphore =
NULL;/*定义信号量句柄*/DWORD WINAPI ThreadFunction(LPVOID param){ int* id = (int*)param; long result = 0
; while (semaphore_num < 100) { WaitForSingleObject(hSemaphore, INFINITE); printf("thread %d use semaphore_num: %d\n"
, *id, semaphore_num); ++semaphore_num; ReleaseSemaphore(hSemaphore, 1, &result); Sleep(1000
); } returnNULL; } intmain(){ HANDLE hThread[5] = { NULL }; int thread[5] = { 0 }; DWORD thread_id[
5] = { 0 }; hSemaphore = CreateSemaphore(NULL, 1, 100, L"sema"); for (int i = 0; i < 5; ++i) { thread[i] = i +
1; hThread[i] = CreateThread(NULL, 0, ThreadFunction, &thread[i], 0, &thread_id[i]); } Sleep(
1000); for (int i = 0; i < 5; ++i) { WaitForSingleObject(hThread[i], INFINITE); } CloseHandle(hSemaphore);
return0; }Event事件(Event):是WIN32提供的最灵活的线程间同步方式,事件可以处于激发状态(signaled or true)或未激发状态(unsignal or false)根据状态变迁方式的不同,事件可分为两类:。
手动设置:这种对象只可能用程序手动设置,在需要该事件或者事件发生时,采用SetEvent及ResetEvent来进行设置自动恢复:一旦事件发生并被处理后,自动恢复到没有事件状态,不需要再次设置【创建事件】。
HANDLE WINAPI CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName ); 参数说明: //lpEventAttributes 安全配置相关参数,采用默认安全配置时传入NULL //bManualReset 传入TRUE时创建manual-
reset模式的事件对象,传入FALSE时创建auto-reset模式的事件对象 //bInitialState 传入TRUE时创建signaled状态,传入FALSE时创建non-signaled状态的事件对象 //lpName 用于命名事件对象。
传递NULL时创建无名的事件对象当第二个参数传入TRUE时将创建manual-reset模式的事件对象,此时即使WaitForSingleObject函数返回也不会回到non-signaled状态因此,在这种情况下,需要通过如下2个函数明确更改对象状态。
【打开事件】HANDLEWINAPI OpenEvent(DWORDdwDesiredAccess,BOOLbInheritHandle,LPCSTRlpName);【复位事件】BOOL WINAPI
ResetEvent(HANDLE hEvent);【设置事件】BOOL WINAPI SetEvent(HANDLE hEvent);传递事件对象句柄并希望改为non-signed状态时,应调用ResetEvent函数。
如果希望改为signaled状态,则可以调用SetEvent函数【示例】#include#include#include#include
#includeint event_num = 1; /*定义全局变量*/ HANDLE hEvent = NULL;/*定义事件句柄*/ DWORD WINAPI ThreadFunction(LPVOID param) {
int* id = (int*)param; long result = 0; while (event_num < 100) { WaitForSingleObject(hEvent, INFINITE); printf(
"thread %d use semaphore_num: %d\n", *id, event_num); ++event_num; SetEvent(hEvent); Sleep(
1000); } returnNULL; } int main() { HANDLE hThread[5] = { NULL }; int thread[5] = { 0 }; DWORD thread_id[
5] = { 0 }; hEvent = CreateEvent(NULL, FALSE, TRUE, L"event"); for (int i = 0; i < 5; ++i) { thread[i] = i +
1; hThread[i] = CreateThread(NULL, 0, ThreadFunction, &thread[i], 0, &thread_id[i]); } Sleep(
1000); for (int i = 0; i < 5; ++i) { WaitForSingleObject(hThread[i], INFINITE); } CloseHandle(hEvent);
return0; }
- 标签:
- 编辑:李松一
- 相关文章
-
李修平三任老公简历(李修平老公简历个人资料简介)学到了
大家心目中的主持人是怎样的?相貌端庄、仪表堂堂、温文尔雅,这些词语形容主持人再好不过而央视一直都是很多播音专业学生梦寐…
-
李修平三任老公简历(李修平老公简历个人资料简介)学到了吗
2015年,做了26年《新闻联播》主持人的李修平,突然之间销声匿迹,央视再也没有了她的踪影,对此不少人纷纷猜测李修平已经退休,毕竟作为一…
- 历任湖南省委书记(历任湖南省委书记省人大常委会主任)没想到
- 男军女嫁(男军女嫁简介)学到了吗
- dreamware(dreamweaver的三种视图模式)满满干货
- 少年阿b(少年游在线阅读)干货分享
- 黑亮综艺(黑亮综艺没了)这都可以