多线程编程--5种方法实现线程同步

1:用Interlocked系列函数实现线程同步;

2:用CRITICAL_SECTION及其系列函数实现线程同步;

3:用RTL_SRWLOCK及其系列函数实现线程同步;

4:用事件内核对象实现线程同步;

5:用信号量内核对象实现线程同步;

1:用Interlocked系列函数实现线程同步实例如下:

[cpp] view plain copy
  1. //旋转锁  
  2. #include <iostream>   
  3. using namespace std;    
  4. #include <process.h>  
  5. #include <windows.h>   
  6. const int threadNum=10;  
  7. HANDLE hThread[threadNum];  
  8. volatile unsigned int ISOK=0;  
  9. unsigned int _stdcall Interlocked(PVOID threadId)  
  10. {   
  11.     while(InterlockedExchange(&ISOK,1)==1) ;   
  12.     cout<<"线程:"<<*(int*)threadId<<"开始"<<endl;   
  13.     Sleep(100);  
  14.     cout<<"线程:"<<*(int*)threadId<<"结束"<<endl;   
  15.     InterlockedExchange(&ISOK,0);    
  16.     return 0;  
  17. }  
  18.    
  19. void InterlockedTest()  
  20. {  
  21.     int threadId[threadNum];  
  22.     for(int i=0;i<threadNum;i++)  
  23.     {  
  24.         threadId[i]=i+1;  
  25.     }  
  26.     cout<<"1:用Interlocked系列函数实现线程同步"<<endl;  
  27.     for(int i=0;i<10;i++){   
  28.         hThread[i]=(HANDLE)_beginthreadex(NULL, 0, Interlocked,threadId+i, 0, NULL);    
  29.     }  
  30.     WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE);     
  31.     for(int i=0;i<threadNum;i++)  
  32.     {  
  33.         CloseHandle(hThread[i]);  
  34.     }   
  35. }  


多线程编程--5种方法实现线程同步_第1张图片

InterlockedExchange确保以原子的方式操作数据。执行速度非常快,缺点是如果要同步的部分执行的时间比较长的话,while循环会一直轮询操作,浪费CPU的时间,在单核CPU的系统中,可能会出现while一直暂用CPU导致其他线程不能修改ISOK的值,导致不能跳出while循环,出现死循环。还有就是线程的优先级问题也能导致问题。

2:用CRITICAL_SECTION及其系列函数实现线程同步实例如下:

[cpp] view plain copy
  1. //关键段   
  2. #include <iostream>   
  3. using namespace std;    
  4. #include <process.h>  
  5. #include <windows.h>   
  6. const int threadNum=10;  
  7. HANDLE hThread[threadNum];   
  8. CRITICAL_SECTION g_cs;//构造一个CRITICAL_SECTION实例  
  9. unsigned int _stdcall  CriticalSection(PVOID threadId)  
  10. {   
  11.     EnterCriticalSection(&g_cs);//进入关键段  
  12.     cout<<"线程:"<<*(int*)threadId<<"开始"<<endl;   
  13.     Sleep(100);  
  14.     cout<<"线程:"<<*(int*)threadId<<"结束"<<endl;   
  15.     LeaveCriticalSection(&g_cs);//进入关键段   
  16.     return 0;  
  17. }  
  18.   
  19.   
  20. void CriticalSectionTest()  
  21. {  
  22.     int threadId[threadNum];  
  23.     for(int i=0;i<threadNum;i++)  
  24.     {  
  25.         threadId[i]=i+1;  
  26.     }  
  27.     InitializeCriticalSection(&g_cs);//初始化g_cs的成员   
  28.     cout<<"2:用CRITICAL_SECTION及其系列函数实现线程同步"<<endl;  
  29.     for(int i=0;i<10;i++){   
  30.         hThread[i]=(HANDLE)_beginthreadex(NULL, 0, CriticalSection,threadId+i, 0, NULL);    
  31.     }  
  32.     WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE);     
  33.     for(int i=0;i<threadNum;i++)  
  34.     {  
  35.         CloseHandle(hThread[i]);  
  36.     }   
  37.     DeleteCriticalSection(&g_cs);//删除关键段  
  38. }  


多线程编程--5种方法实现线程同步_第2张图片

CRITICAL_SECTION同样是以原子的方式操作数据,也只有以原子的方式操作数据才能实现线程的同步,所有实现线程同步的方法,最核心的部分就是以原子的方式操作数据,CRITICAL_SECTION执行的速度非常快,其内部有一个事件内核对象,当出现资源争夺的时候,才会出现初始化这个事件内核对象,由于CRITICAL_SECTION执行非常快可能不会出现资源争夺,也就没有必要创建这个事件内核对象,这个事件内核对象创建后,会将当前线程之外的线程挂起,并记录这些线程需要这个资源,其他线程就不会浪费CPU的时间,而这些被挂起的线程将由用户模式变成内核模式,当这些线程需要的资源可用时,系统会将其中一个线程唤醒。

还有一点值得注意:如果要同步的代码执行得很快,在出现争夺资源的时候,系统把其他线程挂起,而当前线程又马上执行完成了,系统又将挂起的线程唤醒,这个过程是非常浪费CPU的,也影响程序的性能,为了避免这种情况,可以结合旋转锁和CRITICAL_SECTION,先用旋转锁轮询一定次数,还不能获得资源,再将线程挂起,等待资源被释放,系统再将线程唤醒,实现这一功能的就是方法

InitializeCriticalSectionAndSpinCount(

   LPCRITICAL_SECTION lpCriticalSection,

   DWORD dwSpinCount//旋转锁轮询的次数

);

除了初始化CRITICAL_SECTION用的是方法InitializeCriticalSectionAndSpinCount,而不是方法InitializeCriticalSection,其他的都是一样的。

3:用RTL_SRWLOCK及其系列函数实现线程同步实例如下:

[cpp] view plain copy
  1. //读写锁   
  2. #include <iostream>   
  3. using namespace std;    
  4. #include <process.h>  
  5. #include <windows.h>   
  6. const int threadNum=10;  
  7. HANDLE hThread[threadNum];   
  8. RTL_SRWLOCK  lock;//构造一个CRITICAL_SECTION实例  
  9. unsigned int _stdcall  SrwLock(PVOID threadId)  
  10. {   
  11.     AcquireSRWLockExclusive(&lock);//进入读写锁  
  12.     cout<<"线程:"<<*(int*)threadId<<"开始"<<endl;   
  13.     Sleep(100);  
  14.     cout<<"线程:"<<*(int*)threadId<<"结束"<<endl;   
  15.     ReleaseSRWLockExclusive(&lock);//进入读写锁  
  16.     return 0;  
  17. }  
  18.    
  19. void SrwLockTest()  
  20. {  
  21.     int threadId[threadNum];  
  22.     for(int i=0;i<threadNum;i++)  
  23.     {  
  24.         threadId[i]=i+1;  
  25.     }  
  26.     InitializeSRWLock(&lock);//初始化lock的成员   
  27.     cout<<"3:用RTL_SRWLOCK及其系列函数实现线程同步"<<endl;  
  28.     for(int i=0;i<10;i++){   
  29.         hThread[i]=(HANDLE)_beginthreadex(NULL, 0, SrwLock,threadId+i, 0, NULL);    
  30.     }  
  31.     WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE);     
  32.     for(int i=0;i<threadNum;i++)  
  33.     {  
  34.         CloseHandle(hThread[i]);  
  35.     }   
  36.        
  37. }  

多线程编程--5种方法实现线程同步_第3张图片

SRWLock的目的和关键段是一样的,就是对资源的保护,不让其他线程访问。不同的是,它区分线程是读线程还是写线程。我们都是知道,一个资源可以同时被多个线程同时读,就是不能同时读,或是读写。也是是说写必须是独占的方式,而读可以以共享的方式访问,如果以共享的方式访问肯定就比CRITICAL_SECTION性能好。

4:用事件内核对象实现线程同步实例如下:

[cpp] view plain copy
  1. //事件  
  2. #include <iostream>   
  3. using namespace std;    
  4. #include <process.h>  
  5. #include <windows.h>   
  6. const int threadNum=10;  
  7. HANDLE hThread[threadNum];  
  8. HANDLE event1;   
  9.   
  10. unsigned int _stdcall  Event(PVOID threadId)  
  11. {  
  12.     WaitForSingleObject(event1,INFINITE);  
  13.     int* p=(int*)threadId;  
  14.     cout<<"线程:"<<*p<<"开始"<<endl;   
  15.     Sleep(100);  
  16.     cout<<"线程:"<<*p<<"结束"<<endl;    
  17.     SetEvent(event1);  
  18.     return 1;  
  19. }  
  20.   
  21. void EventTest()  
  22. {  
  23.     int threadId[threadNum];  
  24.     for(int i=0;i<threadNum;i++)  
  25.     {  
  26.         threadId[i]=i+1;  
  27.     }  
  28.     event1=CreateEvent(NULL,false,true,NULL);   
  29.     cout<<"4:用事件内核对象实现线程同步"<<endl;     
  30.     for(int i=0;i<threadNum;i++)  
  31.     {  
  32.         hThread[i] =(HANDLE)_beginthreadex(NULL, 0, Event ,threadId+i, 0, NULL);    
  33.     }  
  34.     WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE);    
  35.     for(int i=0;i<threadNum;i++)  
  36.     {  
  37.         CloseHandle(hThread[i]);  
  38.     }   
  39.     CloseHandle(event1);  
  40. }  


用内核对象实现线程同步,一个函数是必须知道的,它就是WaitForSingleObject。

DWORD WaitForSingleObject(

    HANDLE hHandle,//内核对象的句柄

    DWORD dwMilliseconds//等待时间

);

该函数会一直等待,直到被指定的内核对象被触发为止,或是等待的时间结束返回。

CreateEvent(

    LPSECURITY_ATTRIBUTES lpEventAttributes,//安全控制

    BOOL bManualReset,//true:手动重置事件,false:自动重置事件

    BOOL bInitialState,//true:有信号,false:无信号

    LPCWSTR lpName//事件名称

);

bManualReset为true表示事件触发了并一直处于触发状态,就像打开的门,打开之后就是一直开着,没有自动关上;false:一打开放一个进去进关了,需要用SetEvent再次触发事件。

5:用信号量内核对象实现线程同步实例如下:

[cpp] view plain copy
  1. //信号量  
  2. #include <iostream>   
  3. using namespace std;    
  4. #include <process.h>  
  5. #include <windows.h>   
  6. const int threadNum=10;  
  7. HANDLE hThread[threadNum];  
  8. HANDLE semaphore;   
  9. unsigned int _stdcall  Semaphore(PVOID threadId)  
  10. {  
  11.     WaitForSingleObject(semaphore, INFINITE);    
  12.     cout<<"线程:"<<*(int*)threadId<<"开始"<<endl;   
  13.     Sleep(100);  
  14.     cout<<"线程:"<<*(int*)threadId<<"结束"<<endl;   
  15.     ReleaseSemaphore(semaphore,1,NULL);   
  16.     return 0;  
  17. }  
  18.    
  19. void SemaphoreTest()  
  20. {  
  21.     int threadId[threadNum];  
  22.     for(int i=0;i<threadNum;i++)  
  23.     {  
  24.         threadId[i]=i+1;  
  25.     }  
  26.     semaphore=CreateSemaphore(NULL,1,1,NULL);  
  27.     cout<<"5:用信号量内核对象实现线程同步"<<endl;  
  28.     for(int i=0;i<10;i++){   
  29.         hThread[i]=(HANDLE)_beginthreadex(NULL, 0, Semaphore,threadId+i, 0, NULL);    
  30.     }  
  31.     WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE);     
  32.     for(int i=0;i<threadNum;i++)  
  33.     {  
  34.         CloseHandle(hThread[i]);  
  35.     }  
  36.     CloseHandle(semaphore);  
  37. }  


多线程编程--5种方法实现线程同步_第4张图片

信号量内核对象用来对资源进行计数。创建信号量内核对象的方法如下:

CreateSemaphore(

    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//安全控制

    LONG lInitialCount,//初始资源数量

    LONG lMaximumCount,//最大并发数量

    LPCWSTR lpName//号量的名称

);

lMaximumCount表示最大并发数量,可以用来设置系统的最大并发数量,如果我们把他的值设为1,lInitialCount也设为1,就是只有一个资源,且每次只能一个线程访问,这样就可以实现线程同步。

在实现线程同步时,建议用方法2和方法3,如不能解决你的需求,再用方法4,方法5,用内核对象实现的线程同步性能要差一些。

你可能感兴趣的:(线程同步)