📄 16.txt
字号:
16.2.2 利用关键代码段实现线程同步
码段实现线程同步
下面就利用关键代码段来实现线程同步。首先新建一个空的 Wm32 Console Application 类型的工
程,工程名取为: Critical。并为该工程添加一个 C I I源文件: Critical.cpp,然后就在此文件
中添加具体的实现代码,可以复制上面编写的 Event.cpp文件中的内容,并删除其中与事件对象相
关的代码,然后添加使用临界区对象实现线程同步的代码,结果如例 16-4所示。
fJlJ16-4
#include <windows.h>
#include <iostream.h>
DWORD W工 NAPI FunlProc( LPVOID lpParameter // thread data
DWORD W工 NAPI Fun2Proc( LPVO工 D lpParameter // thread data
int tickets=lOOi
CR工 T工 CAL_SECTION .g_cSi
void main()
HANDLE hThreadl;
HANDLE hThread2;
hThreadl=CreateThread(NULL, O, FunlProc , NULL , O, NULL);
hThread2=CreateThread(NULL, O, Fun2Proc , NULL , 0 , NULL);
CloseHandle(hThreadl);
CloseHandle(hThread2);
InitializeCriticalSection(&g一cs) ;
Sleep(4000);
DeleteCriticalSection(&g_cs);
DWORD WINAPI FunlProc( . LPVO工 D lpParameter // thread data
while (TRUE)
{
EnterCriticalSection(&g_cs);
Sleep(l);
if(tickets>O)
{
Sleep(l) ;
cout<<"threadl sell ticket : "<<tickets--<<endl;
LeaveCriticalSection(&g一cs) ;
}
else
LeaveCriticalSection(&g_cs) ;
break;
return 0;
DWORD WINAP工 Fu口 2Proc( LPVOID lpParameter // thread data
while(TRUE)
{
EnterCriticalSection(&g_cs);
~~ I 601
第16章线程罔步
S1eep (1) ;
工f(t工ckets>O}
S1eep(1} ;
cout<< "thread2 se11 ticket : "<<tickets--<<end1 ;
LeaveCritica1Section(&g_cs) ;
e1se
LeaveCritica1Section(&g一cs} ;
break;
return 0;
在上述如例 16-4所示代码中与Event程序相同的部分就不再讲述,这里仅解释新添加
的与关键代码段相关的代码。因为多个线程都需要访问临界区对象,所以将它定义为全局对象,即
上述代码中定义的 CRITICAL SECTION类型的对象: g_cs。
然后按照上面讲述的关键段代码段使用的过程,在 main函数中首先需要调用
InitializeCriticalSection函数创建临界区对象,并在程序退出前,需要调用
DeleteCriticalSection函数释放没有被任何线程使用的临界区对象的所有资源。
在主线程创建的两个线程中,在进入关键代码段访问受保护的代码之前,需要调用
EnterCriticalSection函数,以判断能否得到指定的临界区对象的所有权,如果无法得到该所有权,
那么 EnterCriticalSection函数会一直等待,从而导致线程暂停运行;如果能够得到该所有权,那
么该线程就进入到关键代码段中,访问受保护的资源。当该访问完成之后,需要调用
LeaveCriticalSection函数,释放指定的临界区对象的所有权。
Build并运行Critical程序,将会看到程序结果正常。
在使用临界区对象编程时,有一点需要注意,有时在得到临界区对象的所有权之后,可能忘记释放
该所有权,这将造成什么样的后果呢?我们可以把如例 16-4所示代码中线程1函数FunlPrω中的
LeaveCriticalSection函数调用注释起来,然后再次运行Critical程序,将会看到始终是线程1在销
售火车票,线程2没有得到销售的机会,程序结果如图 16.3所示。
图 16.3线程l没有释放临界区对象所有权时的程序结果
602 I ~~~
-1 VC++深λ详解
为了验证线程2确实没有得到执行关键代码段的机会,在如例 16-4所示代码中线程2函数Fun2Proc
中,在while循环结束后,输出一句话,即在该函数的retum语旬之前添加下面这条语句:
cout<<"thread2 is runningl"<<endl;
如果线程2得到了执行关键代码段的机会,它就会打印出这句话:"tkead2is running!"。
再次运行Critical程序,将会发现始终没有看到"thread2 is running! "这句话,这说明线程2始终
没有得到执行关键代码段的机会。这主要是因为线程l获得了临界区对象的所有权,虽然线程l执行
完成之后就退出了,但是因为该线程一直未释放临界区对象的所有权,导致线程2始终无法得到该所
有权,只能一直等待,无法执行下面的关键代码段,直到进程退出时,该线程也就退出了。这就好
像在公用电话亭使用电话这种资源时,当一个人打完电话离开电话亭走了,但由于某种原因,他把
电话亭锁起来了,这时其他想要进入该电话亭使用电话的人始终判断出该电话亭有人,一直进不去,
虽然实际上这时先前使用电话的那个人已经走了。同样地,这时虽然线程1的运行己经终止并退出了,
但线程2仍然无法得到运行的机会。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -