本文共 2326 字,大约阅读时间需要 7 分钟。
由于需要在多线程中并发操作临界数据,为了保证临界数据操作的完整性,Linux下使用锁(Linux下锁可以看我的这篇博客),而在Windows下,使用的是临界区。
每个线程中访问临界资源的那段程序称为临界区(Critical Section)(临界资源是一次仅允许一个线程使用的共享资源)。每次只准许一个线程进入临界区,进入后不允许其他线程进入。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。在说临界区之前,我们先讲下同步和互斥,以理解为什么需要临界区。
同步和互斥的概念有时候很容易混淆,可以简单地认为同步是更加宏观角度的一种说法,互斥是冲突解决的细节方法。所谓同步就是调度者让任务按照约定的合理的顺序进行,但是当任务之间出现资源竞争,也就是竞态冲突时,使用互斥的规则强制约束允许数量的任务占用资源,从而解决各个竞争状态,实现任务的合理运行。
同步和互斥密不可分,有资料说互斥是一种特殊的同步,对此我不太理解,不过实际中想明白细节就行,文字游戏没有意义。
简单来说:
整个协调流程涉及的角色本质上只有三类:
调度者需要为多个运行任务制定访问使用规则来实现稳定运行,这个调度者可以是内核、可以是应用程序,具体场景具体分析。
要很好地理解同步和互斥,就必须得搞清楚几个重要术语:
最早听说这个术语是在模电数电的课程上,门电路出现竞态条件造成错误的结果,在计算机里面就是多个使用者同时操作共享的变量造成结果的不确定。
临界区域critical section是指多使用者可能同时共同操作的那部分代码,比如自加自减操作,多个线程处理时就需要对自加自减进行保护,这段代码就是临界区域。
Linux下有递归锁,递归锁是同一个线程在不解锁的情况下,可以多次获取锁定同一个递归锁,而且不会产生死锁。windows下的互斥量和临界区(关键段)默认支持递归锁。
void DeleteCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);删除临界区对象释放由该对象使用的所有系统资源
void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );//进入临界区,相当于Linux下lock
void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection );//删除临界区,相当于Linux下unlock
#include#include #include int counter = 0;// 定义一个临界区变量CRITICAL_SECTION g_cs;void doit(void* arg){ int i, val; for (i=0; i<5000; i++) { // 临界区默认支持递归锁,所以这里在临界区里再次进入临界区也没关系,正常使用调用一次即可 EnterCriticalSection(&g_cs); EnterCriticalSection(&g_cs); val = counter; printf("thread %d : %d\n", int(arg), val+1); counter = val + 1; // 离开临界区 LeaveCriticalSection(&g_cs); LeaveCriticalSection(&g_cs); }}int main(int argc, char*argv[]){ // 初始化临界区 InitializeCriticalSection(&g_cs); HANDLE hThread1 = CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)doit, (void*)1, 0, NULL); HANDLE hTrehad2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)doit, (void*)2, 0, NULL); WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hTrehad2, INFINITE); // 删除临界区 DeleteCriticalSection(&g_cs); return 0;}
使用临界区加1次锁和2次锁,均可以正确的输出1~10000。
转载地址:http://phulz.baihongyu.com/