2023年6月22日发(作者:)
解决多线程编程中的同步互斥问题⼀、解决多线程编程中的同步互斥问题,
1、可以使⽤关键段CRITICAL_SECTIONI来解决。
2、关键段CRITICAL_SECTION⼀共有四个函数,分为初始化,销毁,进⼊关键区域、离开关键区域。(关键段⼀般⽤CS代替)
(1)、初始化关键段函数:
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
函数说明:定义关键段变量后必须先初始化
(2)、 关键段销毁函数:
void DeleteCriticalSection(LPCRITICAL lpCriticalSection);
函数说明:关键段⽤完之后记得要销毁 (3)、进⼊关键段区域函数:
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
函数说明:系统保证各线程互斥的进⼊关键区域 (4)、离开关键段区域函数:
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);3、关键段区域⽰例代码: #include int nThreadNum = *(int *)pPM; LeaveCriticalSection(&g_csThreadParameter); //离开⼦线程序号关键区域 Sleep(50); EnterCriticalSection(&g_csThreadCode); //进⼊各⼦线程互斥区域 g_nNum++; Sleep(0); printf("线程编号为%d 全局资源值为%dn", nThreadNum, g_nNum); LeaveCriticalSection(&g_csThreadCode); return 0;} /* 该段程序运⾏两次的结果如下: 1、 经典线程同步 关键段 线程编号为2 全局资源值为1 线程编号为7 全局资源值为2 线程编号为10 全局资源值为3 线程编号为10 全局资源值为4 线程编号为10 全局资源值为5 线程编号为9 全局资源值为6 线程编号为6 全局资源值为7 线程编号为3 全局资源值为8 线程编号为9 全局资源值为9 线程编号为10 全局资源值为102、 经典线程同步 关键段 线程编号为10 全局资源值为1 线程编号为10 全局资源值为2 线程编号为10 全局资源值为3 线程编号为10 全局资源值为4 线程编号为8 全局资源值为5 线程编号为9 全局资源值为6 线程编号为10 全局资源值为7 线程编号为10 全局资源值为8 线程编号为7 全局资源值为9 线程编号为3 全局资源值为10总结:使⽤关键段可以解决当多个线程需要访问同⼀段代码⽚段的情况,但是⽆法解决当多个线程同步运⾏的情况。 通过分析上⾯两次情况: 第⼀种情况:在主进程中先创建了线程1但是并没有进⼊Fun函数,再创建线程2,此时进⼊了Fun函数,随后,主进程创建线程3,4,5,6都没有进⼊ Fun函数,当创建线程7的时候,此时线程7进⼊Fun函数。所以该种⽅法不能解决多个线程访问同⼀段代码⽚段的情况. 缺点:当创建好⼀个线程后,这个线程可以访问多次Fun函数⽽不只是⼀次。(即不能解决同步 */⼆、解决多线程问题使⽤事件Event 1、由于使⽤关键段的“线程所有权”特性所以关键段只能⽤于线程的互斥⽽不能⽤于同步。可以⽤事件Event来解决线程同步的问题。 2、事件Event实际上是个内核对象。在多线程问题中设置⼀个事件和⼀个关键段。⽤事件处理主线程与⼦线程的同步,⽤关键段来处理个⼦线程间的互斥。 (1)、创建事件函数 HANDLE CreateEvent(LPSECRITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTRlpName); 第⼀个参数表⽰安全控制,⼀般直接传⼊NULL。 第⼆个参数确定事件是⼿动置位还是⾃动置位。如果为⾃动置位,则对该事件调⽤ WaitForSingleObject()后会⾃动使事件变成未触发状态。 ⼿动置位相当于教室门,教室门⼀旦打开(被触发),所有⼈都可以进⼊直到⽼师去关上教室门(事件变成未触发). ⾃动置位事件就相当于医院拍X光的房间门,门打开后只能进⼊⼀个⼈,这个⼈进去后就把门关上,其他⼈不能进⼊除⾮门重新被打开(事件重新被触发) 总结:⼿动置位相当于⼈进房间后,可能不关门,这是其他⼈都可以进⼊房间,直到门被关上。⾃动置位相当于⼀个⼈进去房间之后马上就把门关上了,只有它⼀个⼈在房间⾥⾯,其他⼈想要进房间只有等房间⾥⾯的⼈出来,把门打开后才能进去。 第三个参数表⽰事件的初始状态,传⼊TRUE表⽰已触发。 第四个参数表⽰事件的名称,传⼊NULL表⽰匿名事件。 (2)、打开事件函数: 函数功能:根据名称获得⼀个事件句柄。 函数原型: HANDLE OpenEvent(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName); 第⼀个参数表⽰访问权限,对事件⼀般传⼊EVENT_ALL_ACCESS。 第⼆个参数表⽰事件句柄继承性,⼀般传⼊TRUE。 第三个参数表⽰名称,不同进程中的各线程可以通过名称来确保他们访问同⼀个事件。 (3)、触发事件函数: 函数原型:BOOL SetEvent(HANDLE hEvent); 函数说明:每次触发后,必有⼀个或多个处于等待状态下的线程变成可调度状态。 (4)、将事件设置为未触发函数 函数原型:BOOL ResetEvent(HANDLE hEvent); #include system("pause"); return 0;}unsigned int __stdcall Fun(void *pPM){ int nThreadNum = *(int *)pPM; SetEvent(g_hThreadEvent); //触发事件 Sleep(50); EnterCriticalSection(&g_csThreadCode); //进⼊各⼦线程互斥区域 g_nNum++; Sleep(0); printf("线程编号为%d 全局资源值为%dn", nThreadNum, g_nNum); LeaveCriticalSection(&g_csThreadCode); return 0;} /* 以下是我执⾏三次的结果: 1、 经典线程同步 事件Event 线程编号为9 全局资源值为1 线程编号为7 全局资源值为2 线程编号为8 全局资源值为3 线程编号为6 全局资源值为4 线程编号为5 全局资源值为5 线程编号为3 全局资源值为6 线程编号为2 全局资源值为7 线程编号为4 全局资源值为8 线程编号为1 全局资源值为9 线程编号为0 全局资源值为102、 经典线程同步 事件Event 线程编号为9 全局资源值为1 线程编号为7 全局资源值为2 线程编号为8 全局资源值为3 线程编号为6 全局资源值为4 线程编号为5 全局资源值为5 线程编号为3 全局资源值为6 线程编号为2 全局资源值为7 线程编号为4 全局资源值为8 线程编号为1 全局资源值为9 线程编号为0 全局资源值为103、 经典线程同步 事件Event 线程编号为9 全局资源值为1 线程编号为7 全局资源值为2 线程编号为8 全局资源值为3 线程编号为6 全局资源值为4 线程编号为5 全局资源值为5 线程编号为4 全局资源值为6 线程编号为3 全局资源值为7 线程编号为2 全局资源值为8 线程编号为1 全局资源值为9 线程编号为0 全局资源值为10总结:从这三次结果中发现,每个线程他都只访问了Fun⼀次,但是每个线程访问的顺序不是固定的 这说明事件很好的解决了多个线程访问同⼀段代码⽚段会出现重复访问的问题。*/3、解决多线程问题使⽤信号量Semaphore (1)、信号量Semaphore常⽤的三个函数: CreateSemaphore:创建信号量 OpenSemaphore:打开信号量 ReleaseSemaphore:释放信号量 (2)、当前资源数量⼤于0,表⽰信号量处于触发,等于0表⽰资源已经耗尽,资源已经耗尽,故信号量处于未触发。 (3)、在对信号量调⽤等待函数时,等待函数会检查信号量的当前资源计数,如果⼤于0(即信号量处于触发状态),减1后返回让调⽤线程继续执⾏。 (4)、⼀个线程可以多次调⽤等待函数来减少信号量。创建信号量函数: 函数原型: HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONGlMaximumCount,LpCTSTR lpName); 参数说明: 第⼀个参数表⽰安全控制,⼀般直接传⼊NULL。 第⼆个参数表⽰初始资源数量。 第三个参数表⽰最⼤并发数量。 第四个参数表⽰信号量的名称,传⼊NULL表⽰匿名信号量。打开信号量函数: 函数原型: HANDLE OpenSemaphore(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName); 参数说明: 第⼀个参数表⽰访问权限,⼀般传⼊SEMAPHORE_ALL_ACCESS。 第⼆个参数表⽰信号量句柄继承性,⼀般传⼊TRUE。 第三个参数表⽰名称,不同进程中的各线程可以通过名称来确保它们访问同⼀个信号量。ReleaseSemaphore 函数功能:递增信号量的当前资源计数 函数原型: BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount); 参数说明: 第⼀个参数是信号量的句柄。 第⼆个参数表⽰增加个数,必须⼤于0且不超过最⼤资源数量。 第三个参数可以⽤来传出先前的资源计数,设为NULL表⽰不需要传出#include /* 执⾏效果与事件处理的类似,不过是采⽤信号量来解决的.信号量:当前资源数量⼤于0,表⽰信号量处于触发,等于0表⽰资源已经耗尽,故信号量处于未出发通过打断点发现编译器是先产⽣了所有的线程,产⽣完后,向后⾯执⾏的,我觉的这个地⽅可能是采⽤了队列,从⽽解决多个线程同步的问题 在等待单对象函数时,它⼀直处于监听状态,当发现信号量⼤于1了,他就会⽴即执⾏。 */
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1687428800a9340.html
评论列表(0条)