简单的多线程并发同步演示(4种同步方法)

转自: http://blog.csdn.net/morewindows/article/details/7392749

  相交进程之间的关系主要有两种,同步与互斥。所谓互斥,是指散步在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它 们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。所谓同步,是指散步在不同进程之间的若干程序片断,它们的运行必须严格按照规定的 某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。

  _beginthreadex()函数在创建新线程时会分配并初始化一个_tiddata块。这个_tiddata块自然是用来存放一些需要线程独享的数据。事实上新线程运行时会首先将_tiddata块与自己进一步关联起来。然后新线程调用标准C运行库函数如strtok()时就会先取得_tiddata块的地址再将需要保护的数据存入_tiddata块中。这样每个线程就只会访问和修改自己的数据而不会去篡改其它线程的数据了。因此,如果在代码中有使用标准C运行库中的函数时,尽量使用_beginthreadex()来代替CreateThread()。

一、互斥量Mutex

适用范围:可以跨进程同步,还可以用来保证程序只有一个实例运行(创建命名互斥量),也可以用来做线程间的同步

#include "stdafx.h"
#include <iostream>
#include <Windows.h>

using namespace std;

HANDLE hMutex;

DWORD WINAPI Fun(LPVOID lp){
    while(1){
        WaitForSingleObject(hMutex,INFINITE);
        cout<<"fun"<<endl;//如果不用信号量同步,则endl输出会不稳定,或者改成“fun ”
        Sleep(1000);
        ReleaseMutex(hMutex);
    }
}

int main(){
    HANDLE hThread=CreateThread(NULL,0,Fun,NULL,0,NULL);
    hMutex=CreateMutex(NULL,FALSE,"screen");//如创建进程希望立即拥有互斥体,则设为TRUE,一个互斥体同时只能由一个线程拥有。如果是FALSE,表示刚刚创建的这个Mutex不属于任何线程 也就是没有任何线程拥有他,一个Mutex在没有任何线程拥有他的时候,他是处于激发态的, 所以处于有信号状态。
    CloseHandle(hThread);
    while (1)
    {
        WaitForSingleObject(hMutex,INFINITE);
        cout<<"main"<<endl;
        Sleep(1000);
        ReleaseMutex(hMutex);
    }
}

二、内核事件Event 

适用范围:多用于线程间的通信,可以跨进程同步。

 CreateEvent

函数功能:创建事件

函数原型:

HANDLECreateEvent(

 LPSECURITY_ATTRIBUTESlpEventAttributes,

 BOOLbManualReset,

 BOOLbInitialState,

 LPCTSTRlpName

);

函数说明:

第一个参数表示安全控制,一般直接传入NULL

第二个参数确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。打个小小比方,手动置位事件相当于教室门,教室门一旦打开(被触发),所以有人都可以进入直到老师去关上教室门(事件变成未触发)。自动置位事件就相当于地铁进站口,闸机一次只能进入一个人,其他人不能进入除非闸机重新被打开(事件重新被触发)。

第三个参数表示事件的初始状态,传入TRUR表示已触发。

第四个参数表示事件的名称,传入NULL表示匿名事件。

 

//下面程序实现同步,可对比下一个程序(只实现了互斥,没有实现同步)

#include "stdafx.h"
#include <Windows.h>
#include <process.h>
#include <iostream>

using namespace std;
CRITICAL_SECTION cs;
DWORD d=0;
HANDLE hEvent;
unsigned int CALLBACK thread_proc(LPVOID params)
{
  int num=*(int *)params;
  SetEvent(hEvent);//触发事件
  EnterCriticalSection(&cs);
  d++;    //也可用InterlockedIncrement(&d)来执行原子操作
  cout<<"线程编号是:"<<num<<",全局编号是:"<<d<<endl; //运行结果线程编号是唯一的,说明同步了
  LeaveCriticalSection(&cs);
  return 0;
}
int main(int argc, char* argv[]){

  const int THREAD_NUM=10;
  HANDLE handle[THREAD_NUM];

  hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);//创建事件

  InitializeCriticalSection(&cs);

  for(int i=0;i<10;i++){
    handle[i]=(HANDLE)_beginthreadex(NULL,0,thread_proc,&i,NULL,NULL);
    WaitForSingleObject(hEvent,INFINITE);//等待事件触发
  }
  WaitForMultipleObjects(THREAD_NUM,handle,TRUE,INFINITE);
  cout<<d<<endl;

  system("pause");
  return 0;
}

三、临界区 CRITICAL_SECTION

适用范围:它只能同步一个进程中的线程,不能跨进程同步。一般用它来做单个进程内的代码快同步,效率比较高。

//只实现了互斥

#include "stdafx.h"
#include <Windows.h>

#include <process.h> //_beginthreadex函数的头文件
#include <iostream>

using namespace std;
CRITICAL_SECTION cs;

DWORD d=0;

unsigned int CALLBACK thread_proc(LPVOID params){

  

  int num=*(int *)params;

  EnterCriticalSection(&cs);
  d++;
  cout<<"线程编号是:"<<num<<",全局编号是:"<<d<<endl; //线程编号没有顺序,见下面解释
  LeaveCriticalSection(&cs);


  return 0;
}
int main(int argc, char* argv[]){

  const int THREAD_NUM=3;
  HANDLE handle[THREAD_NUM];
  InitializeCriticalSection(&cs);

  for(int i=0;i<3;i++){

    handle[i]=(HANDLE)_beginthreadex(NULL,0,thread_proc,&i,NULL,NULL); //传入的线程编号 i 并不会按顺序显示,因为创建一个线程的同时 i 已经变成2或者3了
  }


  WaitForMultipleObjects(THREAD_NUM,handle,TRUE,INFINITE);//等待所以线程结束

  system("pause");
  return 0;
}

 四、 信号量Semaphore

   * Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行。
    * Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于N=1的情况,称为binary semaphore。一般的用法是,用于限制对于某一资源的同时访问。

原文地址:https://www.cnblogs.com/duyy/p/3716752.html