和我一起住的同学从毕业开始就一直在使用COBOL语言。这是很老的一种开发语言了,当时被称为恐龙级别的东西。开始我不能理解为什么这么老的语言还一直在用呢。慢慢的从我同学那里知道,他们做的业务都是银行、证券、保险方面的项目。于是,我搜索了一下之后才知道,这是一种适用于商业和数据处理的一种语言。有一种说法是”银行不到,就有COBOL”呵呵。(是不是有点跑题了,呵呵。别着急,等等)
去年我同学换了一个新工作,是给全球最大的保险公司做项目。随之而来的就是更新鲜的事情了。他们做开发的时候,只能在本机上进行编码,如果需要编译或调试的话,就要链接到外国的服务器上。于是,他们在本机上编码后都要反复的Review,然后用自己的账号登陆到外国服务器,进行编译。有一个问题,一直让他很郁闷。每次只能有一个可以链接到外国的服务器上,如果其他人想使用,就只能排队等待。当某一开发人员退出编译环境时,另一个人就可以占用编译器进行工作了。
每天下班回家后,都和我抱怨,这项目做的真麻烦,都啥时代了,怎么还想80年代似的,编译都要排队。
等一下,故事先讲到这里吧。大家是不是觉得他们这种方式有点像我们说过的线程同步呢。再想一下,是不是还有点像曾经说过的信号量呢。只不过,信号量是可以多个线程同时访问一个资源,而这里是只能有一个开发人员占用编译器。那么如果在创建信号量的时候,把信号量的值设置为1,就符合现在这个故事了。
在操作系统知识体系中存在一个互斥(Mutex)的概念。互斥也被当作二元信号灯。一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源。
这里有涉及到了一个新名词----临界区,那么什么是临界区呢。
临界区:需要被多个线程访问的那段代码称为临界区(Critical Section)。
Windows系统中给我提供了一个CreateMutex()函数,用来创建一个互斥量。互斥也属于系统内核对象,创建成功后函数会返回一个句柄。同信号量一样,互斥不仅可以使用在多线程中,也可以在多进程中使用。函数声明如下:
HANDLE WINAPI CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName);
lpMutexAttributes:设定安全属性。多数情况成NULL。
bInitialOwner:如果设置为TRUE,那么创建该互斥的线程拥有这个互斥量。
LpNames:为创建的互斥量起一个名。其他线程或进程可以通过这个名字找到这个互斥量。
有了互斥量后,其他线程或进程通过OpenMutex()取得这个互斥量的句柄,进行判断是否可以进入临界区。使用后再调用ReleaseMutex()释放使用权。这两个函数声明如下:
HANDLE WINAPI OpenMutex( DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName );
BOOL WINAPI ReleaseMutex( HANDLE hMutex);
除了用在线程同步中,很多情况下也在进程互斥中使用。当不希望用户可以开启两个实例的时候可以在程序的如果处,创建一个互斥量。如果用户开启两个实例的时候,第二个实例重复创建互斥量的时候就会失败,于是就可以断定是用户两次运行程序了。第二个实例的互斥量创建失败后,程序退出。功能完成,呵呵。
例子:
BOOL CTestThreadApp::InitInstance()
{
……
……
CWinApp::InitInstance();
HANDLE hMutex = CreateMutex(NULL, TRUE, APPMUTEX);
if(NULL == hMutex)
{
return FALSE;
}
if(ERROR_ALREADY_EXISTS == ::GetLastError())
{
return FALSE;
}
……
}
请注意:
if(ERROR_ALREADY_EXISTS == ::GetLastError())
{
return FALSE;
}
当有重名的互斥量被创建时,CreateMutex()函数会返回互斥量的句柄。但是通过GetLastError()函数返回Error Code是ERROR_ALREADY_EXISTS。当知道Error Code是ERROR_ALREADY_EXISTS必然是用户启动了两个进程实例,于是后启动的那个实例退出。