Windows线程同步的四种方式

Windows线程同步的四种方式为什么要进行线程同步在多线程的程序中 很少有多个线程能在其生命期内进行完全独立的操作 通常情况是一些线程进行某些操作 而其他的线程必须对其操作后的结果进行了解 如果不采取同步机制 其他线程会在线程处理任务前访问处理结果 这样会产生错误的了解

欢迎大家来到IT世界,在知识的湖畔探索吧!

为什么要进行线程同步

在多线程的程序中,很少有多个线程能在其生命期内进行完全独立的操作;通常情况是一些线程进行某些操作,而其他的线程必须对其操作后的结果进行了解。如果不采取同步机制,其他线程会在线程处理任务前访问处理结果,这样会产生错误的了解。例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题;若一个线程负责写操作,其他线程负责读取操作,则不能保证读取的就是修改过的值,这时就必须在变量写操作过程时加上访问限制,在写操作完成后解除访问限制。这种保证线程能正确获取其他线程处理结束后的结果的措施称为线程同步。

线程同步的四种方式:

同步方式

速度、资源开销

跨进程

资源统计

Critical Section

速度快、非内核对象

不能用于不同进程

不能资源统计(每次只能有一个线程对共享资源进行存取)

Mutex

速度慢,内核对象

可用于不同进程

不能资源统计

Semaphore

速度慢、内核对象

可用于不同进程

可资源统计(可以让一个或多个线程对共享资源进行存取)

Event

速度慢、内核对象

可用于不同进程

可资源统计

Critical Section

临界区(Critical Section):通过对多线程的串行化来访问公共资源或一段代码,本身不是内核对象,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开。临界区被释放后,其他线程才可以抢占。

【初始化临界区】

VOID WINAPI InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection );

欢迎大家来到IT世界,在知识的湖畔探索吧!

【删除临界区】

欢迎大家来到IT世界,在知识的湖畔探索吧!VOID WINAPI DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection );

【获取临界区】

VOID WINAPI EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );

【释放临界区】

欢迎大家来到IT世界,在知识的湖畔探索吧!VOID WINAPI LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection );

临界区在使用时,以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()LeaveCriticalSection()函数占有和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。

【示例】

#include <iostream> #include <cstdlib> #include <cstdio> #include <ctime> #include <windows.h> int 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++; } return 0; } int main() { 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); return 0; }

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时创建无名的互斥量对象

【销毁互斥量】

BOOL WINAPI CloseHandle( HANDLE hObject );

【获取互斥量】

//获取函数 Windows线程创建中介绍的此函数,用于针对单个内核对象验证signaled。 DWORD 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 <iostream> #include <cstdlib> #include <cstdio> #include <ctime> #include <windows.h> int 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++; } return 0; } int main() { 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); return 0; }

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 <iostream> #include <cstdlib> #include <cstdio> #include <ctime> #include <windows.h> int 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); } return NULL; } int main() { 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); return 0; }

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个函数明确更改对象状态。

【打开事件】

HANDLE WINAPI OpenEvent( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName );

【复位事件】

BOOL WINAPI ResetEvent(HANDLE hEvent);

【设置事件】

BOOL WINAPI SetEvent(HANDLE hEvent);

传递事件对象句柄并希望改为non-signed状态时,应调用ResetEvent函数。如果希望改为signaled状态,则可以调用SetEvent函数。

【示例】

#include <iostream> #include <cstdlib> #include <cstdio> #include <ctime> #include <windows.h> int 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); } return NULL; } 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); return 0; }

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/88677.html

(0)
上一篇 2024年 11月 18日 下午7:23
下一篇 2024年 11月 18日 下午8:00

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信