[windows][thread] 同步.非内核.CriticalSection
临界区:在所有同步对象中,临界区是最容易使用的,但它只能用于同步单个进程中的线程。取得对某个数据区的访临界区一次只允许一个线程问权。还有,在这些同步对象中,只有临界区不是内核对象,它不由操作系统的低级部件管理,而且不能使用句柄来操纵,由于不是内核对象,使得它作为一种轻量级的同步机制,同步速度比较快。
使用步骤:
1. 在进程中创建一个临界区,即在进程中分配一个CRITICAL_SECTION数据结构,该临界区结构的分配必须是全局的,这样该进程的不同线程就能访问它。关于CRITICAL_SECTION结构体的深入分析,可以参见文章:
<<Break Free of Code Deadlocks in Critical Sections Under Windows>>
2. 在使用临界区同步线程之前,必须调用InitializeCriticalSection来初始化临界区。在释放资源之前,只需要初始化一次。
3. VOID EnterCriticalSection:阻塞函数。The function returns when the calling thread is granted ownership。换言之,调用线程不能获取指定临界区的所有权时,该线程将睡眠,且在被唤醒之前,系统不会给它分配CPU。或者使用TryEnterCriticalSection方法尝试进入临界区,如果进入成功,则调用者线程获得临界区的使用权,否则返回失败。
4. 执行临界区内的任务
5. BOOL LeaveCriticalSection:非阻塞函数。将当前线程对指定临界区的引用计数减壹;在使用计数变为零时,另一等待此临界区的一个线程将被唤醒。
6. 当不需要再使用该临界区时,使用DeleteCriticalSection来释放临界区需要的资源。此函数执行后,再也不能使用EnterCriticalSection和LeaveCriticalSection,除非再次使用InitializeCriticalSection初始化了该临界区。
1. 临界区一次只允许一个线程访问,每个线程必须在视图操作临界区域数据之前调用该临界区域标志(即一个CRITICAL_SECTION全局变 量)EnterCriticalSection后,其它想要获得访问权的线程都会置于睡眠状态,且在被唤醒以前,系统将停止为它们分配CPU时间片。换言之,临界区可以且仅可被一个线程拥有,当然,没有任何线程调用EnterCriticalSection或TryEnterCriticalSection时,临界区不属于任何 一个线程。
2. 当拥有临界区所有权的线程调用LeaveCriticalSection放弃所有权时,系统只唤醒等待队列中的一个线程,给它所有权,其它线程则继续等待。
3. 注意,拥有该临界区的线程,每一次针对此临界区的EnterCriticalSection调用都会成功(这里指的是重复调用也会立即返回,也就是支持嵌套调用),且会使得临界区标志(即一个CRITICAL_SECTION全局变量)的引用计数增加1。在另一个线程能够拥有该临界区之前,拥有它的线程必须调用LeaveCriticalSection足够多次,在引用计数降为零后,另一线程才有可能拥有该临界区。换言之,在一个正常使用临界区的线程中,calSection和LeaveCriticalSection应该成对使用。
4. TryEnterCriticalSection
BOOL TryEnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
从函数声明便可看出端倪,EnterCriticalSection函数的返回值为VOID,而这里为BOOL。可见对于TryEnterCriticalSection的调用,需要我们判断其返回值。在调用TryEnterCriticalSection时,如果指定的临界区没有被任何线程(或还没有被任何调用线程)拥有,该函数将临界区的访问权给予调用的线程,并返回TRUE;不过,如果临界区已经被另一个线程拥有,它立刻返回FALSE值。TryEnterCriticalSection和EnterCriticalSection之间的最大区别在于TryEnterCriticalSection从来不挂起线程。