在孙鑫VC++6的视频教程中,第十六章:线程同步与异步套接字的例16-4代码如下:
#include <windows.h>
#include <iostream>
using namespace std;//原程序为#include <iostream.h>
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int tickets=100;
CRITICAL_SECTION g_cs;
void 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);
InitializeCriticalSection(&g_cs);
Sleep(4000);
DeleteCriticalSection(&g_cs);
}
DWORD WINAPI Fun1Proc( LPVOID lpParameter )
{
while(TRUE)
{
EnterCriticalSection(&g_cs);
Sleep(1);
if(tickets>0)
{
Sleep(1);
cout<<"thread1 sell ticket : "<<tickets--<<endl;
LeaveCriticalSection(&g_cs);
}
else
{
LeaveCriticalSection(&g_cs);
break;
}
}
return 0;
}
DWORD WINAPI Fun2Proc( LPVOID lpParameter)
{
while(TRUE)
{
EnterCriticalSection(&g_cs);
Sleep(1);
if(tickets>0)
{
Sleep(1);
cout<<"thread2 sell ticket : "<<tickets--<<endl;
LeaveCriticalSection(&g_cs);
}
else
{
LeaveCriticalSection(&g_cs);
break;
}
}
return 0;
}
该段代码在VC++6.0中运行完好,没有问题,线程1和线程2交互卖票。但是当把这段程序移植到VS2010平台上之后,运行会出现问题:线程1一直在买票,线程2没有得到买票的机会。当设置相应断点之后会发现,线程2根本没有得到临界区对象的所有权,即程序根本没有进入到线程2的EnterCriticalSection(&g_cs)函数中,程序一直在线程1中运行。并且会发现,线程1的if语句中的LeaveCriticalSection(&g_cs)函数没起什么什么作用,释放临界区对象所有权之后,该所有权马上又被线程1获得,因此线程2不可能获得临界区的所有权。
修改以后的代码如下:
#include <stdafx.h>
#include <windows.h>
#include <iostream>
using namespace std;
DWORD WINAPI ThreadProc1(LPVOID lpParameter);
DWORD WINAPI ThreadProc2(LPVOID lpParameter);
int ticket=100;
CRITICAL_SECTION g_cs;
int main()
{
HANDLE handle1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
HANDLE handle2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
CloseHandle(handle1);
CloseHandle(handle2);
InitializeCriticalSection(&g_cs);
Sleep(4000);
DeleteCriticalSection(&g_cs);
getchar();//为使程序结果长时间显示
return 0;
}
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
while(TRUE)
{
Sleep(1);
EnterCriticalSection(&g_cs);
if(ticket>0)
{
Sleep(1);
cout<<"thread1 sale the ticket id is:"<<ticket--<<endl;
LeaveCriticalSection(&g_cs);
}
else
{
LeaveCriticalSection(&g_cs);
break;
}
}
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
while(TRUE)
{
Sleep(1);
EnterCriticalSection(&g_cs);
if(ticket>0)
{
Sleep(1);
cout<<"thread2 sale the ticket id is:"<<ticket--<<endl;
LeaveCriticalSection(&g_cs);
}
else
{
LeaveCriticalSection(&g_cs);
break;
}
}
return 0;
}
主要修改的是:EnterCriticalSection(&g_cs); Sleep(1);的前后位置;将其修改为Sleep(1); EnterCriticalSection(&g_cs);
上述代码中,线程1,2先卖票先后顺序不定。并且当改变线程的sleep时间后,程序运行时卖票发生问题,不是交替卖票
或者也可以将代码修改如下:
#include <stdafx.h>
#include <windows.h>
#include <iostream>
using namespace std;
DWORD WINAPI ThreadProc1(LPVOID lpParameter);
DWORD WINAPI ThreadProc2(LPVOID lpParameter);
int ticket=100;
CRITICAL_SECTION g_cs;
int main()
{
HANDLE handle1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
HANDLE handle2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
CloseHandle(handle1);
CloseHandle(handle2);
InitializeCriticalSection(&g_cs);
Sleep(4000);
DeleteCriticalSection(&g_cs);
getchar();
return 0;
}
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
while(TRUE)
{
EnterCriticalSection(&g_cs);
Sleep(1);
if(ticket>0)
{
Sleep(1);
cout<<"thread1 sale the ticket id is:"<<ticket--<<endl;
LeaveCriticalSection(&g_cs);
Sleep(1);
}
else
{
LeaveCriticalSection(&g_cs);
Sleep(1);
break;
}
}
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
while(TRUE)
{
EnterCriticalSection(&g_cs);
Sleep(1);
if(ticket>0)
{
Sleep(1);
cout<<"thread2 sale the ticket id is:"<<ticket--<<endl;
LeaveCriticalSection(&g_cs);
Sleep(1);
}
else
{
LeaveCriticalSection(&g_cs);
Sleep(1);
break;
}
}
return 0;
}
不改变EnterCriticalSection(&g_cs); Sleep(1);在源代码中前后位置,但要在if语句LeaveCriticalSection(&g_cs);的后面加上 Sleep(1); 这样线程2就能获得临界区的所有权。
注:关于有些网页上说的问题出在InitializeCriticalSection(&g_cs);这句代码上,说这段代码一定要在线程创建之前,我试验了一下,没有解决问题。InitializeCriticalSection(&g_cs);这句代码和线程没有太大的先后位置关系。
注:若改变线程的创建先后顺序,线程的执行顺序会发生变化。在第二种方法中:线程1先卖票,然后交替卖票,若先创建线程2,则线程2先卖票。