线程同步的几种方式

 在多线程程序设计里,线程同步是非常重要的,基本的方法有

1.互斥对象

2.事件对象

3.信号量

4.临界区

 

在说到线程同步的时候就不能不介绍一下WAIT函数

WAIT函数是在等待的对象处于激发状态时候才能返回,常见的WAIT函数有

WaitForSingleObject,WaitForMultipleObjects,MsgWaitForMultipleObjects

所以了解等待对象的何时处于激发状态是正确使用WAIT函数的关键,在介绍同步方法的时候,我会简单的说明。

 

在介绍这四种同步方法的时候,我使用了一个很经典的售票的例子,

int _tmain(int argc, _TCHAR* argv[])
{

 HANDLE  hThread[2];
hThread[0] = CreateThread(NULL,0,SellTicket,0,0,NULL);
 hThread[1] = CreateThread(NULL,0,SellTicket,0,0,NULL);
 WaitForMultipleObjects(2,hThread,TRUE,INFINITE);

 CloseHandle(hThread[0]);
 CloseHandle(hThread[1]);

 return 0;
}

DWORD __stdcall SellTicket(LPVOID lpParam)
{
 while(Ticket>0)
 {
  cout<<"The "<<Ticket--<<" Ticker sold!"<<endl;
 }
  return 0;
}

对于这个例子我们都知道如果不采用线程同步的方法,两个线程之间就会发生一些无法预料的事情。

对我来说最习惯使用的是事件对象来实现线程的同步,设定一个全局的事件对象

HANDLE     h_Event = NULL;

int _tmain(int argc, _TCHAR* argv[])
{

 HANDLE  hThread[2];

h_Event = CreateEvent(NULL,FALSE,TRUE,NULL);
hThread[0] = CreateThread(NULL,0,SellTicket,0,0,NULL);
 hThread[1] = CreateThread(NULL,0,SellTicket,0,0,NULL);
 WaitForMultipleObjects(2,hThread,TRUE,INFINITE);

 CloseHandle(hThread[0]);
 CloseHandle(hThread[1]);

 CloseHandle(h_Event);

 return 0;
}

DWORD __stdcall SellTicket(LPVOID lpParam)
{

WaitForSingleObject(h_Event,INFINITE);
 while(Ticket>0)
 {
  cout<<"The "<<Ticket--<<" Ticker sold!"<<endl;
 }

SetEvent(h_Event);
  return 0;
}

这样如果一个线程没有返回时,另一个线程就会被进入等待状态,而不能进行售票的动作。最后我们要对事件对象调用CloseHandle()销毁事件对象。

 

临界区域变量不属于内核对象,它也能对包含同一个资源的代码段进行同步保护,

它相当于占有了那一段代码的所有权,当它还没有离开的时候,别的线程就进不去那段代码段。

(感觉这也是多线程设计的一个优点,每个线程都可以进程的代码段资源)

CRITICAL_SECTION cs;

int _tmain(int argc, _TCHAR* argv[])
{

 HANDLE  hThread[2];

InitializeCriticalSection(&cs);

hThread[0] = CreateThread(NULL,0,SellTicket,0,0,NULL);
 hThread[1] = CreateThread(NULL,0,SellTicket,0,0,NULL);
 WaitForMultipleObjects(2,hThread,TRUE,INFINITE);

 CloseHandle(hThread[0]);
 CloseHandle(hThread[1]);

DeleteCriticalSection(&cs);

 return 0;
}

DWORD __stdcall SellTicket(LPVOID lpParam)
{

EnterCriticalSection(&cs)
 while(Ticket>0)
 {
  cout<<"The "<<Ticket--<<" Ticker sold!"<<endl;
 }

LeaveCriticalSection(&cs);
  return 0;
}

在使用临界区域变量的时候要进行初始化,当不使用的时候要对销毁掉临界区域变量。

如果以一个线程进入临界区域,那么一直重复的进入该临界区域。(这里说的该临界区域是指同一个临界区域变量所有的代码段,那些代码段可以分别在不同的函数里,不需要是连续的)

由于临界区域变量不是内核对象,加入一个线程进入了之后就死了,而没有离开临界区结果就会锁住那块代码段,并且系统也不能清除掉临界区域变量,最后的结果可能就是程序挂在那,求生不得,求死不能。

 

/*------------------------------这里还有一个使用临界区变量思索的问题-------------------------------

---------------------------------回过头再来讨论这个问题----------------------------------------------

--------------主要是因为临界区变量不是内核对象我们无法想其他内核对象一样,通过WAIT函数知道是否被激发------*/

 

所以我要介绍下一个对象是互斥对象,它是内核对象,可以解决临界区域变量的一些问题,但它也有一些问题(别着急,那些问题我也正在思考!)

 

互斥对象可以对资源同步操作,互斥对象在没有任何线程拥有时候处于激发状态,准确的说是在没有任何线程拥有时,被Wait..函数等待的时候,短暂的处于激发状态,让Wait...函数返回,同时该线程拥有互斥对象的所有权,记住,一个时间内只能有一个线程拥有互斥对象。

HANDLE   h_Mutex;

int _tmain(int argc, _TCHAR* argv[])
{

 HANDLE  hThread[2];

h_Mutex = CreateMutex(NULL,FALSE,NULL);

hThread[0] = CreateThread(NULL,0,SellTicket,0,0,NULL);
 hThread[1] = CreateThread(NULL,0,SellTicket,0,0,NULL);
 WaitForMultipleObjects(2,hThread,TRUE,INFINITE);

 CloseHandle(hThread[0]);
 CloseHandle(hThread[1]);

 CloseHandle(h_Mutex);

 return 0;
}

DWORD __stdcall SellTicket(LPVOID lpParam)
{

WaitForSingleObject(h_Mutex,INFINITE);
 while(Ticket>0)
 {
  cout<<"The "<<Ticket--<<" Ticker sold!"<<endl;
 }

 ReleaseMutex(h_Mutex);
  return 0;
}

当不再需要互斥对象的时候,要释放互斥对象。

如果一个线程没有释放那个互斥对象,那么那个互斥对象会被舍弃处理,如果有其他线程等待,WAIT函数会返回WAIT_OBANDONED_0到WAIT_OBANDONED_0+N之间的一个数,我们就能知道那个互斥对象被舍弃了

 

问题也产生了:知道了哪个互斥对象被舍弃了又能怎么样呢?这个一个问题,那些受保护的数据又该怎么保护,比起临界区来说,至少程序不会挂在那,因为WAIT函数至少还能返回,但是安全问题依然没有解决。

 

信号量是我感觉比较麻烦的一个,semaphore创建的时候可以指定对一种资源进行占有的线程数,就是说信号量可以锁你指定的次数,互斥对象其实是它的特殊情况,当对一份资源进行占有的线程数为1的时候其实就是互斥对象了。当一个线程锁住一份资源的时候,信号量就会减1,当减到0的时候,就要等某个线程释放了那个资源其他的线程才能对资源进行占有了。

为什么需要信号量呢,因为有时候我们的某种资源是很多很多滴,太多了,以至于为每个资源都捆绑一个互斥对象显得太麻烦,麻烦就是个问题,谁也不想太麻烦。其实还有其他原因。。。不想说了。

 

我要想一个比较什么例子来使用信号量,写个售票和退票的例子也许是个不错的尝试

 

 

int  Ticket = 100;

HANDLE   h_SemaphoreSell;

HANDLE h_SemaphoreDishonor;

HANDLE h_Event;

DWORD dwStart;

 


DWORD __stdcall SellTicket(LPVOID lpParam);

DWORD __stdcall DishonorTicket(LPVOID lpParam);

 


int _tmain(int argc, _TCHAR* argv[])

{

 


 HANDLE  hThread[6];

 dwStart = GetTickCount();

 


 h_SemaphoreSell = CreateSemaphore(NULL,50,100,NULL);

 h_SemaphoreDishonor = CreateSemaphore(NULL,0,100,NULL);

 h_Event = CreateEvent(NULL,FALSE,TRUE,NULL);

 


 hThread[0] = CreateThread(NULL,0,SellTicket,0,0,NULL);

 hThread[1] = CreateThread(NULL,0,SellTicket,0,0,NULL);

 hThread[2] = CreateThread(NULL,0,SellTicket,0,0,NULL);

 hThread[3] = CreateThread(NULL,0,SellTicket,0,0,NULL);

 

 

 

 hThread[4] = CreateThread(NULL,0,DishonorTicket,0,0,NULL);

 hThread[5] = CreateThread(NULL,0,DishonorTicket,0,0,NULL);

 WaitForMultipleObjects(6,hThread,TRUE,INFINITE);

 


 CloseHandle(hThread[0]);

 CloseHandle(hThread[1]);

 CloseHandle(hThread[2]);

 CloseHandle(hThread[3]);

 CloseHandle(hThread[4]);

 CloseHandle(hThread[5]);

 


 CloseHandle(h_SemaphoreSell);

 CloseHandle(h_SemaphoreDishonor);

 


 return 0;

}

 


DWORD __stdcall SellTicket(LPVOID lpParam)

{

 DWORD dwResult;

 while(1)

 {

  WaitForSingleObject(h_Event,INFINITE);

  dwResult = WaitForSingleObject(h_SemaphoreSell,3000);

  if(WAIT_OBJECT_0 == dwResult)

  {

   cout<<"The "<<Ticket--<<" Ticket sold!"<<endl;

   ReleaseSemaphore(h_SemaphoreDishonor,1,NULL);

  }

  else if(WAIT_TIMEOUT == dwResult)

  {

   ;

  }

  SetEvent(h_Event);

 }

 return 0;

}

 


DWORD __stdcall DishonorTicket(LPVOID lpParam)

{

 while(1)

 {

  WaitForSingleObject(h_Event,INFINITE);

  WaitForSingleObject(h_SemaphoreDishonor,INFINITE);

  cout<<"The "<<++Ticket<<" Ticket dishonored"<<endl;

  ReleaseSemaphore(h_SemaphoreSell,1,NULL);

  SetEvent(h_Event);

 }

 

    return 0;

 


}


我设了2个信号量,1个是售票的,1个是退票的,开始的时候我把售票信号量初始值设为50(那些走关系的就先把这50张票拿走了,就把50张拿来出售了,的我们只能排队慢慢等了)当卖出一张票的时候售票信号量就锁住,同时释放退票信号量,
同理,当退出一张票的时候退票信号量就锁住,同时释放售票信号量。我创建了4个卖票线程,2个退票线程,毕竟是买票更不好买啊!

当卖完50张的时候,就要等待有人退票才能买到票了,这个例子不怎么好,因为最后总是一个人在卖第51张票,又在不断的退第51张票。继续努力中。。。。。。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/jxcyly1985/archive/2008/09/05/2886793.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值