VC下线程同步的三种方法(互斥、事件、临界区)

VC下线程同步的三种方法(互斥、事件、临界区)   

2008-01-25 09:57:59|  分类: VC |  标签: |举报 |字号 订阅


首选使用临界区对象,主要原因是使用简单。
EnterCriticalSection()函数等候指定的危险区段对象的所有权。当调用的线程被允许所有权时,函数返回。
EnterCriticalSection(),一个单独进程的线程可以使用一个危险区段对象作为相互-排除同步。 进程负责分配被一个危险区段对象使用的内存, 它藉由声明一个CRITICAL_SECTION类型 的变量实现。在使用一个危险区段之前,进程的一些线程必须调用 InitializeCriticalSection 函数设定对象的初值.
为了要使互斥的访问被共享的资源,每个线程调用EnterCriticalSection 或者 TryEnterCriticalSection 功能,在执行访问被保护资源的任何代码段之前,请求危险区段的所有权。

#include <windows.h>

#include <iostream>

using namespace std;

DWORD WINAPI Fun1Proc(LPVOID lpParameter);

DWORD WINAPI Fun2Proc(LPVOID lpParameter);

int tickets=100;

CRITICAL_SECTION g_csA;

CRITICAL_SECTION g_csB;

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_csA);

 InitializeCriticalSection(&g_csB);

 Sleep(40000);

 DeleteCriticalSection(&g_csA);

 DeleteCriticalSection(&g_csB);

}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)

{

 while (TRUE)

 {

  EnterCriticalSection(&g_csA);

  Sleep(1);

  //EnterCriticalSection(&g_csB);//临界区的同步和互锁

  if (tickets>0)

  {

   Sleep(1);

   cout<<"Thread1 sell ticket :"<<tickets--<<endl;

   //LeaveCriticalSection(&g_csB);

   LeaveCriticalSection(&g_csA);

  }

  else

  {

   //LeaveCriticalSection(&g_csB);

   LeaveCriticalSection(&g_csA);

   break;

  }

 }

 return 0;

}

DWORD WINAPI Fun2Proc(LPVOID lpParameter)

{

 while (TRUE)

 {

  EnterCriticalSection(&g_csB);

  Sleep(1);

  EnterCriticalSection(&g_csA);

  if (tickets>0)

  {

   Sleep(1);

   cout<<"Thread2 sell ticket :"<<tickets--<<endl;

   LeaveCriticalSection(&g_csA);

   LeaveCriticalSection(&g_csB);

  }

  else

  {

   LeaveCriticalSection(&g_csA);

   LeaveCriticalSection(&g_csB);

   break;

  }

 }

 return 0;

}


--------------------------------------------------------------------------------

 

二、使用互斥对象
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
如果时间是有信号状态返回WAIT_OBJECT_0,如果时间超过dwMilliseconds值但时间事件还是无信号状态则返回WAIT_TIMEOUT
WaitForSingleObject函数用来检测hHandle事件的信号状态,当函数的执行时间超过dwMilliseconds就返回,但如果参数dwMilliseconds为INFINITE时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到WaitForSingleObject有返回直才执行后面的代码。

#include <windows.h>

#include <iostream>

using namespace std;

DWORD WINAPI Fun1Proc(LPVOID lpParameter);

DWORD WINAPI Fun2Proc(LPVOID lpParameter);

int index =0;

int tickets=100;

HANDLE hMutex;

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);

 //**************************************************************

 //保证应用程序只有一个实例运行,创建一个命名的互斥对象.

 hMutex=CreateMutex(NULL,TRUE,LPCTSTR("tickets"));

 //创建时主线程拥有该互斥对象,互斥对象的线程ID为主线程的ID,同时将该互斥对象内部计数器置为1

 if (hMutex)

 {

  if (ERROR_ALREADY_EXISTS==GetLastError())

  {

   cout<<"only one instance can run!"<<endl;

   //Sleep(40000);

   return;

  }

 }

 //**************************************************************

 WaitForSingleObject(hMutex,INFINITE);

 //使用该函数请求互斥对象时,虽说该对象处于无信号状态,但因为请求的线程ID和该互斥对象所有者的线程ID是相同的.所以仍然可以请求到这个互斥对象,于是该互斥对象内部计数器加1,内部计数器的值为2. 意思是有两个等待动作

 ReleaseMutex(hMutex);//释放一次互斥对象,该互斥对象内部计数器的值递减1,操作系统不会将这个互斥对象变为已通知状态.

 ReleaseMutex(hMutex);//释放一次互斥对象,该互斥对象内部计数器的值为0,同时将该对象设置为已通知状态.

 //对于互斥对象来说,谁拥有谁释放

 Sleep(40000);

}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)

{

 while (TRUE)

 {

  WaitForSingleObject(hMutex,INFINITE);//等待互斥对象有信号

  if (tickets>0)

  {

   Sleep(1);

   cout<<"thread1 sell ticket :"<<tickets--<<endl;

  }

  else

   break;

  ReleaseMutex(hMutex);//设置该互斥对象的线程ID为0,并且将该对象设置为有信号状态

 }

 return 0;

}

DWORD WINAPI Fun2Proc(LPVOID lpParameter)

{

 while (TRUE)

 {

  WaitForSingleObject(hMutex,INFINITE);

  if (tickets>0)

  {

   Sleep(1);

   cout<<"thread2 sell ticket :"<<tickets--<<endl;

  }

  else

   break;

  ReleaseMutex(hMutex);

 }

 return 0;

}


--------------------------------------------------------------------------------


三、使用事件对象
HANDLE     CreateEvent(   
        LPSECURITY_ATTRIBUTES     lpEventAttributes,           //     SD   
        BOOL     bManualReset,                                                 //     reset     type   
        BOOL     bInitialState,                                                     //     initial     state   
        LPCTSTR     lpName                                                      //     object     name   
    );   
    该函数创建一个Event同步对象,并返回该对象的Handle   
   
    lpEventAttributes     一般为NULL   
    bManualReset               创建的Event是自动复位还是人工复位     ,如果true,人工复位,   
    一旦该Event被设置为有信号,则它一直会等到ResetEvent()API被调用时才会恢复   
    为无信号.     如果为false,Event被设置为有信号,则当有一个wait到它的Thread时,   
    该Event就会自动复位,变成无信号.   
    bInitialState             初始状态,true,有信号,false无信号   
    lpName                           Event对象名   
   
    一个Event被创建以后,可以用OpenEvent()API来获得它的Handle,用CloseHandle()   
    来关闭它,用SetEvent()或PulseEvent()来设置它使其有信号,用ResetEvent()   
    来使其无信号,用WaitForSingleObject()或WaitForMultipleObjects()来等待   
    其变为有信号.   
   
    PulseEvent()是一个比较有意思的使用方法,正如这个API的名字,它使一个Event   
    对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的.   
    对自动复位的Event对象,它仅释放第一个等到该事件的thread(如果有),而对于   
    人工复位的Event对象,它释放所有等待的thread.  

#include <windows.h>
#include <iostream>
using namespace std;
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int tickets=100;
HANDLE g_hEvent;

void main()
{
 HANDLE hThread1;
 HANDLE hThread2;
 //**************************************************
 //创建一个命名的自动重置事件内核对象
 g_hEvent=CreateEvent(NULL,FALSE,FALSE,LPCTSTR("tickets"));
 if (g_hEvent)
 {
  if (ERROR_ALREADY_EXISTS==GetLastError())
  {
   cout<<"only one instance can run!"<<endl;
   return;
  }
 }
 //**************************************************
 SetEvent(g_hEvent);
 //创建线程
 hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
 hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

 Sleep(40000);
 //关闭事件对象句柄
 CloseHandle(g_hEvent);
}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
 while (TRUE)
 {
  WaitForSingleObject(g_hEvent,INFINITE);
  //ResetEvent(g_hEvent);
  if (tickets>0)
  {
   Sleep(1);
   cout<<"thread1 sell ticket :"<<tickets--<<endl;
   SetEvent(g_hEvent);
  }
  else
  {
   SetEvent(g_hEvent);
   break;
  }
 }
 return 0;
}

DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
 while (TRUE)
 {
  WaitForSingleObject(g_hEvent,INFINITE);
  //ResetEvent(g_hEvent);
  if (tickets>0)
  {
   cout<<"Thread2 sell ticket :"<<tickets--<<endl;
   SetEvent(g_hEvent);
  }
  else
  {
   SetEvent(g_hEvent);
   break;
  }
 }
 return 0;
}


原文:http://blog.163.com/junior_zhong/blog/static/27871180200802595759702/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
VC中,可以使用三种线程同步方法,分别是临界区(CriticalSection)、事件(Event)、互斥体(Mutex)。下面是一个使用这三种方法的多线程程序的示例: #include <windows.h> #include <iostream> using namespace std; int g_iCount = 0; CRITICAL_SECTION g_cs; //定义临界区 HANDLE g_hEvent; //定义事件句柄 HANDLE g_hMutex; //定义互斥体句柄 DWORD WINAPI ThreadFunc(LPVOID lpParam) { for (int i = 0; i < 100000; i++) { EnterCriticalSection(&g_cs); //进入临界区 g_iCount++; //临界区内进行操作 LeaveCriticalSection(&g_cs); //退出临界区 SetEvent(g_hEvent); //设置事件 WaitForSingleObject(g_hMutex, INFINITE); //等待互斥体 g_iCount--; //互斥体中进行操作 ReleaseMutex(g_hMutex); //释放互斥体 } return 0; } int main(int argc, char* argv[]) { HANDLE hThread[2]; DWORD dwThreadId[2]; InitializeCriticalSection(&g_cs); //初始化临界区 g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //创建事件 g_hMutex = CreateMutex(NULL, FALSE, NULL); //创建互斥体 for (int i = 0; i < 2; i++) { hThread[i] = CreateThread(NULL, 0, ThreadFunc, NULL, 0, &dwThreadId[i]); //创建线程 } for (int i = 0; i < 2; i++) { WaitForSingleObject(hThread[i], INFINITE); //等待线程结束 } cout << "iCount = " << g_iCount << endl; DeleteCriticalSection(&g_cs); //删除临界区 CloseHandle(g_hEvent); //关闭事件句柄 CloseHandle(g_hMutex); //关闭互斥体句柄 return 0; } 上面的程序中,首先定义了一个全局变量g_iCount,用于计数。然后定义了一个临界区g_cs、一个事件句柄g_hEvent、一个互斥体句柄g_hMutex。在主函数中,首先初始化临界区、创建事件句柄、创建互斥体句柄。然后创建两个线程,每个线程都调用ThreadFunc函数。在ThreadFunc函数中,通过EnterCriticalSection和LeaveCriticalSection进入和退出临界区。通过SetEvent设置事件,并通过WaitForSingleObject等待互斥体。通过ReleaseMutex释放互斥体。最后主函数等待两个线程结束,并输出计数结果。最后删除临界区,关闭事件句柄和互斥体句柄。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值