注释很详细,利用了
互斥对象实现线程同步
#include <windows.h> //1.用到了WinAPI的函数
#include <iostream> //2.用到了C++标准输入输出流函数using namespace std;
DWORD WINAPI Fun1Proc(LPVOID lpParameter); //8.线程函数可以直接在MSDN上拷,这里是做以个函数的申明
DWORD WINAPI Fun2Proc(LPVOID lpParameter); //22.线程2的函数申明
int index=0; //14.定义一个 变量来控制循环
int tickets=100; //24.定义票的总数
HANDLE hMutex; //35.
void main() //3.main函数就是主线程的入口函数
{
HANDLE hThread1; //4.定义一个句柄,在main函数中创建一个新的线程,要创建一个线程可以利用系统给我们提供的API函数CreateThread
HANDLE hThread2; //19.创建第二个线程
hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); //5.Fun1Proc线程入口函数的地址
hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); //20将线程2的地址加入主线程中
CloseHandle(hThread1); //6.关闭线程句柄
CloseHandle(hThread2); //21.关闭线程2的句柄
//while(index++<1000) //15.当index小于1000的时候执行
//cout<<"main thread is running!"<<endl; //7.输出一行字,表明是主线程在运行
//Sleep(10); //13.让主线程停止执行,即这10毫秒内主线程放弃执行的权利,运行发现两个都运行了,下面要让主线程和线程1不断的交替运行,我们可以做一个循环
//16.这个时候因为主线程执行的时间较长,这个时间已经大于了系统分配给主线程的时间片,所以就有执行其他线程的机会了
//hMutex=CreateMutex(NULL,FALSE,NULL); //36.创建互斥对象,FALSE(表示主线程不拥有) 将它的ID设置为空,即此时谁都没有拥有这个线程ID,系统会自动设为0,处于有信号状态,继续向下执行,
//42.如果这里设置为TRUE,那么主线程拥有互斥对象此时计数器记录为1,调用一个ReleaseMutex释放之后计数器又记录为0,
//43.此时信号处于可用状态,其他的线程可以使用,每申请一次就加1,释放就减1,为0则可用
//44.如果我们将最后一个NULL(没有名字)去掉,并且给这个互斥对象取名
hMutex=CreateMutex(NULL,TRUE,"tickets"); //45.给这个互斥对象取名 将中间的设置为TRUE
if(hMutex) //46.判断对象是否已经存在,如果存在就执行下面的
if(ERROR_ALREADY_EXISTS==GetLastError()) //47.判断实例是否正在运行
{
cout<<"Only one instance can run!"<<endl;
return;
}
ReleaseMutex(hMutex); //48.释放主线程互斥对象,在10s内连开多个实例只有第一个可以顺利运行,这就是互斥对象的用法
Sleep(10000); //27.让主线程睡眠1秒,这个时间必须设置合理,不然线程1、2没运行完就关了进程
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter) //9.实现函数部分
{
/*while(index++<1000) //17.index小于1000的时候执行,执行之后发现它们是交替运行的,说明是按时间片来的,这是单CUP下,如果是双CUP就可以真正的额实现同时运行了
cout<<"thread1 is running!"<<endl;*/
while(TRUE) //24.在线程1里做循环,
{
WaitForSingleObject(hMutex,INFINITE); //37.这个函数是等待接受互斥对象的信号,一旦有信息进入,就向下执行,否则一直等待 因为设置的是这个模式(INFINIT)
if(tickets>0)
{
Sleep(1); //30.Test!
cout<<"Thread1 sell tickets:"<<tickets--<<endl;//25.满足条件就打印这行,表明执行了 线程1
}
else
break;
ReleaseMutex(hMutex); //38.执行完成之后,交出信号,这时系统又将线程ID设置为空,如果线程1的时间片没完,就继续执行线程1,如果线程1执行过程中时间片就到了,这时就跳到执行线程2
//39.但是由于线程1的信号没有交出,所以互斥对象的线程ID还是线程1的,线程2没有得到信号会一直等待,直到时间片结束,转到线程1,执行完成,释放线程ID
}
return 0; //10.要求返回值
} //11.这样运行之后发现只有主线程在运行线程1并不运行,这主要是当主线程运行完之后就将进程给关了,进程关了,其他的线程就没有运行的机会就关闭了
//12.可以让主线程执行完就睡眠一会儿,这样其他的线程就可以执行了
//18.下面来模拟火车站的售票系统,显然它是一个多线程的系统,我们在增加一个线程2
DWORD WINAPI Fun2Proc(LPVOID lpParameter) //23.线程2的函数实现部分
{
while(TRUE) //26.同样作循环,还有一个问题,在执行这两个线程的时候要保证主线程运行,注意不要让主线程做空循环,这样浪费CPU时间,效率不高,让他睡眠比较好
{
WaitForSingleObject(hMutex,INFINITE); //40.等待互斥对象得信号,没有得到信号一直等待,直到时间片完成
if(tickets>0)
{
Sleep(1); //31.Test!
cout<<"Thread2 sell tickets:"<<tickets--<<endl;
}
else
break;
ReleaseMutex(hMutex); //41.释放互斥对象,注意是“谁拥有谁释放”,这样执行之后问题就解决了,多线程访问同一资源的时候经常出现资源重复,这时就要用到互斥对象了,在一个进程正在访问的时候不允许另一个访问
}
return 0; //28.还有一个问题,如果线程1执行卖85张的时候,时间片到了,就会转到线程2中执行,这时线程2中,线程2 就卖出了第85张,过一段时间候线程2的时间到了,
//29.又转到线程1中,线程1继续卖它的85张,这样就可能卖出两张相同的票号的票,这种情况最容易发生在转换时间片的时候,还有可能出第0张票,我们可以将循环睡眠一段时间看看
} //32.这是多线程访问同一种资源造成的,可以看到又很多的重复,还有0也被卖出了,这是不行的!,如果我们能做线程的同步就能解决了
//33.构建一个互斥对象来解决这个问题 CreateMutex函数 互斥对象属于内核对象它能确保线程拥有对单个资源的互斥访问权,它包含一个使用数量、一个线程ID、一个计数器。ID用于标识系统中的哪个线程当前拥有互斥对象
//34.计数器用于指明该线程拥有互斥对象的次数。CreateMutex返回一个句柄
转载出处:http://dongxiang-2007.blog.sohu.com/112078796.html