CCriticalSection g_clsCriticalSection; // MFC临界区类对象
char g_cArray[10]; // 共享资源
UINT ThreadProc20(LPVOID pParam)
{
g_clsCriticalSection.Lock(); // 进入临界区
for (int i = 0; i < 10; i++) // 对共享资源进行写入操作
{
g_cArray[i] = 'a';
Sleep(1);
}
g_clsCriticalSection.Unlock(); // 离开临界区
return 0;
}
UINT ThreadProc21(LPVOID pParam)
{
g_clsCriticalSection.Lock();
for (int i = 0; i < 10; i++)
{
g_cArray[10 - i - 1] = 'b';
Sleep(1);
}
g_clsCriticalSection.Unlock();
return 0;
}
……
void CSample08View::OnCriticalSectionMfc()
{
AfxBeginThread(ThreadProc20, NULL);
AfxBeginThread(ThreadProc21, NULL);
Sleep(300);
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
2.事件内核对象
在前面讲述线程通信时曾使用过事件内核对象来进行线程间的通信,除此之外,事件内核对象也可以通过通知操作的方式来保持线程的同步。对于前面那段使用临界区保持线程同步的代码可用事件对象的线程同步方法改写如下:
char g_cArray[ 10 ]; // 共享资源
UINT ThreadProc12(LPVOID pParam)
{
WaitForSingleObject(hEvent, INFINITE); // 等待事件置位
for ( int i = 0 ; i < 10 ; i ++ )
{
g_cArray[i] = ' a ' ;
Sleep( 1 );
}
SetEvent(hEvent); // 处理完成后即将事件对象置位
return 0 ;
}
UINT ThreadProc13(LPVOID pParam)
{
WaitForSingleObject(hEvent, INFINITE);
for ( int i = 0 ; i < 10 ; i ++ )
{
g_cArray[ 10 - i - 1 ] = ' b ' ;
Sleep( 1 );
}
SetEvent(hEvent);
return 0 ;
}
……
void CSample08View::OnEvent()
{
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // 创建事件
SetEvent(hEvent); // 事件置位
AfxBeginThread(ThreadProc12, NULL); // 启动线程
AfxBeginThread(ThreadProc13, NULL);
Sleep( 300 );
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
在创建线程前,首先创建一个可以自动复位的事件内核对象hEvent,而线程函数则通过WaitForSingleObject()等待函数无限等待hEvent的置位,只有在事件置位时WaitForSingleObject()才会返回,被保护的代码将得以执行。对于以自动复位方式创建的事件对象,在其置位后一被WaitForSingleObject()等待到就会立即复位,也就是说在执行ThreadProc12()中的受保护代码时,事件对象已经是复位状态的,这时即使有ThreadProc13()对CPU的抢占,也会由于WaitForSingleObject()没有hEvent的置位而不能继续执行,也就没有可能破坏受保护的共享资源。在ThreadProc12()中的处理完成后可以通过SetEvent()对hEvent的置位而允许ThreadProc13()对共享资源g_cArray的处理。这里SetEvent()所起的作用可以看作是对某项特定任务完成的通知。
使用临界区只能同步同一进程中的线程,而使用事件内核对象则可以对进程外的线程进行同步,其前提是得到对此事件对象的访问权。可以通过OpenEvent()函数获取得到,其函数原型为: