📄 15.4.3 利用互斥对象实现线程同步.txt
字号:
15.4.3 利用互斥对象实现线程同步
互斥对象 (mutex)属于内核对象,它能够确保线程拥有对单个资源的互斥访问权。互斥对象包含一
个使用数量,一个线程 B和一个计数器。其中 ID用于标识系统中的哪个线程当前拥有互斥对象,计
数器用于指明该线程拥有互斥对象的次数。
为了创建互斥对象,需要调用函数: CreateMutex,该函数可以创建或打开一个命名的或匿名的互斥
对象,然后程序就可以利用该互斥对象完成线程间的同步。 CreateMutex函数的原型声明如下所述:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes ,
BOOL binitial Own er,
LPCTSTR lpNarne ,
该函数具有三个参数,其含义分别如下所述:
. lpMutexAttributes
一个指向 SECURITY ATTRmUTES结构的指针,可以给该参数传递 NULL值,让互斥对象使用默认的安
全性。
. blnitia10wner
BOOL类型,指定互斥对象初始的拥有者。如果该值为真,则创建这个互斥对象的线程获得该对象的
所有权;否则,该线程将不获得所创建的互斥对象的所有权。
. lpName
指定互斥对象的名称。如果此参数为 NULL.则创建一个匿名的互斥对象。
如果调用成功,该函数将返回所创建的互斥对象的句柄。如果创建的是命名的互斥对象,并且在
CreateMutex函数调用之前,该命名的互斥对象存在,那么该函数将返回己经
存在的这个互斥对象的句柄,而这时调用 GetLas tError函数将返回 ERROR_ALREADY_
EXISTS。
另外,当线程对共享资源访问结束后,应释放该对象的所有权,也就是让该对象处于
己通知状态。这时需要调用ReleaseMutex函数,该函数将释放指定对象的所有权。该函数的原型声
明如下所示:
BOOL ReleaseMutex ( HANDLE hMutex );
ReleaseMutex函数只有一个 HANDLE类型的参数,即需要释放的互斥对象的句柄。该函数的返回值是
BOOL类型,如果函数调用成功,返回非0值:否则返回0值。
另外,线程必须主动请求共享对象的使用权才有可能获得该所有权,这可以通过调用
WaitForSingleObject函数来实现,该函数的原型声明如下所述:
DWORD WaitForSingleObject{ HANDLE hHandle , DWORD dwMilliseconds };
该函数有两个参数,其含义分别如下所述:
. hHandle
所请求的对象的句柄。本例将传递己创建的互斥对象的句柄:陆1utex。一旦互斥对象处于有信号状
态,则该函数就返回。如果该互斥对象始终处于无信号状态,即未通知的状态,则该函数就会一直
等待,这样就会暂停线程的执行。
. dwMilliseconds
指定等待的时间间隔,以毫秒为单位。如果指定的时间间隔己过,即使所请求的对象仍处于无信号
状态, WaitForSingleObject函数也会返回。如果将此参数设置为 0,那么 WaitForSingleObject
函数将测试该对象的状态并立即返回;如果将此参数设置为.J..l".1 "U ....1. J. L , 则该函数会
永远等待,直到等待的对象处于有信号状态才会返回。
调用WaitForSingleObject函数后,该函数会一直等待,只有在以下两种情况下才会返回:
·
指定的对象变成有信号状态:
·
指定的等待时间间隔己过。
如果函数调用成功,那么WaitForSingleObject函数的返回值将表明引起该函数返回的事件,表15.1
列出了该函数可能的返回值:
表 15.1 WaitForSingleObject函数的返回值
返回值 说明
WAIT OBJECT 0 所请求的对象是有信号状态
WAIT TIMEOUT 指定的时间间隔己过,并且所请求的对象是无信号状态
WAIT ABANDONED 所谓求的对象是一个互斥对象,并且先前俯有该对象的线程在终止前没旬释放该
对象.这 时,该对象的所有权将授予当前调用线程,并且将该互斥对象被设置为无信号状态
下面,我们修改上述如例 15-2所示的MultiThread程序代码,利用互斥对象来实现线程|司步。最终
的实现代码如例 15-3所示。
例 15-3
#include <windows.h>
#include <iostream.h>
DWORD WINAPI FunlProc(
LPVOID lpParameter 11 thread data
DWORD WINAPI Fu口 2Proc( LPVO工 o lpParameter 11 thread data
int index=0;
int tickets=100;
HANDLE hMutex;
void main ()
{
HANDLE hThread1;
HANDLE hThread2;
11创建线程
hThread1=CreateThread(NULL, 0, Fun1Proc , NULL , 0, NULL) ;
hThread2=CreateThread(NULL, 0 , Fun2Proc , NULL , 0 , NULL) ;
CloseHandle(hThread1) ;
CloseHandle(hThread2) ;
11创建互斥对象
hMutex=CreateMutex(NULL, FALSE , NULL);
Sleep(4000) ;
11线程 1的入口函数
DWORD WINAPI Fun1Proc( LPVOID lpParameter 11 thread data
Wwhile (TRUE)
waitForSingleObject( hMutex,INFINITE) ;
if (tickets>0)
Sleep (1) ;
cout<<"threadl sell ticket : "<<tickets--<<endl;
else
break;
ReleaseMutex(hMutex);
return 0 ;
11线程 2的入口函数
DWORD WINAPI Fun2Proc(
LPVO工D lpParameter // thread data
while{TRUE)
WaitForSingleObject{hMutex,INFINITE) ;
if (tickets>O)
Sleep{1) ;
cout<< "thread2 sell ticket : "<<tickets--<<endl;
else
break:
ReleaseMutex(hMutex);
return 0 ;
在上述例 15-3所示代码中,首先定义了一个HANDLE类型的全局变量: hMutex,用来保存即将创建的
互斥对象句柄。
接下来,在mam函数中,调用CreateMutex函数创建一个匿名的互斥对象。
然后在线程1和线程2中,在需要保护的代码前面添加WaitForSingleObject函数的调
用,让其请求互斥对象的所有权,这样线程1和线程2就会一直等待,除非所请求的对象处于有信号
状态,该函数才会返回,线程才能继续往 F执行,即才能执行受保护的代码。因此,在如例15-3所
示代码中,在线程l和线程2访问它们共享的全局变量tickets之前,添加了下述语句,实现线程同步:
WaitForSingleObject{hMutex,INFINITE) ;
这样,当执行到这条语句时,线程 l和线程2就会等待,除非所等待的互斥对象: hMutex处于有信号
状态,线程才能继续向下执行,即才能访问 tickets变量,完成火车票的销售工作。
当对所要保护的代码操作完成之后,应该调用ReleaseMutex函数释放当前线程对互斥对象的所有权,
这时,操作系统就会将该互斥对象的线程ID设置为0,然后将该互斥对象设置为有信号状态。使得其
他线程有机会获得该对象的所有权,从而获得对共享资源的访问。
下面,我们来分析一下如例 15-3所示程序的执行过程。在创建互斥对象时,第二个参数传递的是
FALSE值,这表明当前没有线程拥有这个互斥对象,于是,操作系统就会将该互斥对象设置为有信号
状态。当第一个线程开始运行时,进入 while循环后,调用 WaitForSingleObject函数,因为这时
互斥对象处于有信号状态,所以该线程就请求到了这个互斥对象,操作系统就会将该互斥对象.的线
程设置为线程1的ID,接着,操作系统会将这个互斥对象设置为未通知状态。线程1继续往下运行,
调用 Sleep函数,于是暂停执行。操作系统就会选择线程 2开始执行,该线程的执行过程也是一样
的,进入到 while
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -