先看售票系统的程序,看看多线程容易出什么问题:
#include <windows.h>
#include <iostream.h>
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int tickets = 100;
int main()
{
HANDLE hThread1;
HANDLE hThread2;
hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
Sleep(4000);
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(1)
{
if(tickets > 0)
{
cout << "thread2 sell ticket : "<< tickets-- << endl;
}
else
{
break;
}
}
return 0;
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while(1)
{
if(tickets > 0)
{
cout << "thread1 sell ticket : " << tickets-- << endl;
}
else
{
break;
}
}
return 0;
}
多运行几次,你会发现,程序不太正常,究竟是什么原因呢?线程不同步(同步的意思是协调,而非同时)。那么如何实现线程同步呢?我们可以利用互斥对象,程序如下:
#include <windows.h>
#include <iostream.h>
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int tickets = 100;
HANDLE hMutex = NULL;
int main()
{
HANDLE hThread1;
HANDLE hThread2;
hMutex = CreateMutex(NULL, FALSE, NULL);
hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
Sleep(4000);
CloseHandle(hMutex);
return 0;
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while(1)
{
WaitForSingleObject(hMutex, INFINITE);
if(tickets > 0)
{
Sleep(1);
cout << "thread1 sell ticket : " << tickets-- << endl;
}
else
{
break;
}
ReleaseMutex(hMutex);
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(1)
{
WaitForSingleObject(hMutex, INFINITE);
if(tickets > 0)
{
Sleep(1);
cout << "thread2 sell ticket : "<< tickets-- << endl;
}
else
{
break;
}
ReleaseMutex(hMutex);
}
return 0;
}
互斥对象是啥?互斥对象就像房子的唯一钥匙。我们来看看语句 WaitForSingleObject(hMutex, INFINITE); 当互斥对象处于无信号状态时, 表明有人在用这个钥匙,所以必须继续等待,当别人释放了这把钥匙的控制权后,互斥对象就会变得有信号,此时继续执行WaitForSingleObject(hMutex, INFINITE);语句后面的语句。
注意弄清下面的几个概念(如果你在火车里面拉过屎,我相信你对互斥对象会有非常深刻的理解):
互斥对象:唯一钥匙。
互斥对象有信号(已通知状态):目前没有人用这个钥匙,故钥匙可用。
互斥对象没有信号(未通知状态):目前有人在用这个钥匙,自己暂时不能用。
好,现在来分析一下上面的程序:
1. 主线程执行main函数,然后创建互斥对象和两个线程,主线程睡4秒钟的懒觉;
2. 线程函数Fun1Proc获得执行,执行WaitForSingleObject(hMutex, INFINITE); 此时,互斥对象是有信号状态,相当于线程hThread1获得了互斥对象,操作系统立即将互斥对象变为无信号状态(让别的线程得不到该互斥对象,所以ticket变量安全了),程序继续向下执行;
3. 线程hThread1睡0.001秒钟的懒觉,hThread2对应的函数Fun2Proc获得执行,其中的WaitForSingleObject(hMutex, INFINITE);得到调用,此时互斥对象处于无信号状态,不可用,因为线程hThread1已经独占了该互斥对象,所以Fun2Proc中的WaitForSingleObject函数就只能等呀等,等者等着,线程hThread1已经从0.001秒的美梦中清醒了,不再睡懒觉了,于是继续执行Sleep后面的语句,这样线程hThread1就卖出了第一张票。
4. 接着,hThread1对应的函数Fun1Proc调用语句ReleaseMutex(hMutex); 释放了互斥对象,此时互斥立即不再为线程hThread1所独占,互斥对象立即变得有信号。这个时候,hThread2中的WaitForSingleObject终于盼到到互斥对象变成有信号,意思是hThread2终于获得了这个互斥对象,此时操作系统又将互斥对象设为无信号状态(让别的线程无法获得该互斥对象,所以ticket变量安全了)Fun2Proc继续执行,从而卖出了第二张票。
接下来的分析就很俗套了。