我们也可以通过临界区保证在同一时间内只有一个线程对共享数据进行控制访问。临界区不是内核对象,只能对进程内部的线程进行同步。
临界区对象是定义在数据段中的一个CRITICAL_SECTION结构,Windows内部使用这个结构记录的一些信息,来确保同一个时间只有一个线程访问该临界区保护的数据。
临界区对象使用要调用的函数接口如下:
void WINAPI InitializeCriticalSection( _Out_ LPCRITICAL_SECTION lpCriticalSection ); //lpCriticalSection:指向临界区对象的指针
创建CRITICAL_SECTION对象后,需要调用该函数进行临界区对象的初始化。任何线程在试图访问临界区所保护的资源之前,CRITICAL_SECTION结构对象的内部成员必须已经初始化。如果线程试图进入一个未经初始化的CRITICAL_SECTION,那么结果是不可预料的。
void WINAPI EnterCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection ); //lpCriticalSection:指向临界区对象的指针
当线程要访问临界区所保护的数据的时候,必须首先调用该函数进入临界区,在同一时间,Windows只运行有一个线程进入临界区,如果调用EnterCriticalSection时,已经有线程在临界区内,那么会调用一个事件内核对象把调用线程切换到等待状态,这样调用线程不会浪费任何CPU时间,系统内核会记住这个线程想要访问的资源,当该资源可用时(另一个使用该资源的线程调用LeaveCriticalSection),系统会将等待中的线程切换回可调度的状态。
BOOL WINAPI TryEnterCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection ); //lpCriticalSection:指向临界区对象的指针 //Return Value:表示是否进入临界区
TryEnterCriticalSection和EnterCriticalSection函数唯一的区别就是调用线程不会进入等待状态。如果共享资源真在被其他线程访问,那么该函数会返回FALSE,表示进入临界区失败,如果返回TRUE,表示已经进入临界区。
void WINAPI LeaveCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection ); //lpCriticalSection:指向临界区对象的指针
当线程完成了对共享资源的访问,应该调用该函数退出临界区,以便其他线程可以进行访问。
void WINAPI DeleteCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection ); //lpCriticalSection:指向临界区对象的指针
当程序不再使用临界区的时候,必须调用该函数将临界区对象删除。
使用临界区对象时有两点要注意:
下面程序测试临界区功能:
#include <iostream> #include <windows.h> #include <process.h> using namespace std; CRITICAL_SECTION csTest; volatile int flag = 0; void CriticalSectionTest1(void *ptr) { EnterCriticalSection(&csTest); flag = 1; for (int i = 0; i < 5; ++i) { Sleep(1000); cout<<flag<<' '; } LeaveCriticalSection(&csTest); } void CriticalSectionTest2(void *ptr) { EnterCriticalSection(&csTest); flag = 2; for (int i = 0; i < 5; ++i) { Sleep(1000); cout<<flag<<' '; } LeaveCriticalSection(&csTest); } int main() { InitializeCriticalSection(&csTest); _beginthread(CriticalSectionTest1, 0, NULL); _beginthread(CriticalSectionTest2, 0, NULL); Sleep(INFINITE); DeleteCriticalSection(&csTest); }
程序运行结果:
2 2 2 2 2 1 1 1 1 1这里从运行结果可以看出,两个线程互斥的访问临界区资源,这里为了测试在进入临界区后调用Sleep函数, 但在实际程序中这种行为会严重影响程序的性能。
Jun 19, 2013 PM 22:58 @dorm