线程同步,如果光从字面上看,这四个字并不好理解。什么叫线程的同步?让线程同时都在运行,显然不是如此。多线程的出现,就是为了让每个子线程做独立的事情,而这里面经常发生的一个问题是,子线程做独立的事情时却要使用同一个资源(即共享资源,常常是全局变量)。所以,我更喜欢称之为线程的协调,使线程协调访问共享资源,而不是在同一时刻访问它。

举个例子,我们平时的火车售票系统,其中定义了一个变量tickets,是全局变量,也是各个子线程都可以访问的共享资源。这个时候,如果有两个子线程同时访问这个变量,势必会出现售票混乱的问题。当然,文字性的叙述永远都不够清晰明了。

实现线程的协调,有三种方式(我现在只知道3种),分别是通过创建互斥对象函数(CreateMutex)、创建事件对象函数(CreateEvent)、初始化临界区对象函数(InitializeCriticalSection)来实现。

 

代码1(创建互斥对象函数):

 
  1. #include <windows.h>  
  2. #include <iostream.h>  
  3.  
  4. int tickets=100; //共享资源  
  5. HANDLE hMutex;   //共享对象(因为是全局变量嘛~)  
  6.  
  7. //线程入口函数的声明  
  8. DWORD WINAPI Func1Proc(LPVOID lpParameter);  
  9. DWORD WINAPI Func2Proc(LPVOID lpParameter);  
  10.  
  11. //主线程  
  12. int main()  
  13. {  
  14.     HANDLE hThread1;  
  15.     HANDLE hThread2;  
  16.  
  17.     /*安全属性(SecurityAttributes),  
  18.      *是否拥有互斥对象(InitialOwner),  
  19.      *是否是匿名的互斥对象(MutexName)  
  20.      */ 
  21.     hMutex=CreateMutex(NULL,FALSE,NULL);   
  22.       
  23.     hThread1=CreateThread(NULL,0,Func1Proc,NULL,0,NULL);  
  24.     hThread2=CreateThread(NULL,0,Func2Proc,NULL,0,NULL);  
  25.     CloseHandle(hThread1);  
  26.     CloseHandle(hThread2);  
  27.       
  28.     Sleep(4000);  
  29.       
  30.     return 0;  
  31. }  
  32.  
  33. //线程1的入口函数  
  34. DWORD WINAPI Func1Proc(LPVOID lpParameter)  
  35. {  
  36.     while(true)  
  37.     {  
  38.         WaitForSingleObject(hMutex,INFINITE);  
  39.         if(tickets>0)  
  40.         {  
  41.            
  42.            cout<<"thread1 sell tickets: "<<tickets--<<endl;  //tickets先输出,再减1  
  43.         }  
  44.         else 
  45.         {  
  46.            break;  
  47.         }  
  48.         ReleaseMutex(hMutex);  
  49.     }  
  50.       
  51.     return 0;  
  52. }  
  53. //线程2的入口函数  
  54. DWORD WINAPI Func2Proc(LPVOID lpParameter)  
  55. {  
  56.     while(true)  
  57.     {  
  58.         WaitForSingleObject(hMutex,INFINITE);  
  59.         if(tickets>0)  
  60.         {  
  61.              
  62.            cout<<"thread2 sell tickets: "<<tickets--<<endl;  //tickets先输出,再减1  
  63.         }  
  64.         else 
  65.         {  
  66.            break;  
  67.         }  
  68.         ReleaseMutex(hMutex);  
  69.     }  
  70.       
  71.     return 0;  
  72. }  

需要注意的是,CreateMutex函数的3个参数,说明如下:

HANDLE CreateMutex(   LPSECURITY_ATTRIBUTES ,   BOOL ,   LPCTSTR  );

第一个参数,表示默认的安全属性;

第二个参数,表示创建互斥对象时主线程是否拥有互斥对象;

第三个参数,互斥对象名

 

代码2(创建事件对象函数):

 
  1. #include <windows.h>  
  2. #include <iostream.h>  
  3.  
  4. int tickets=100; //共享资源  
  5. HANDLE hEvent;   //共享对象(因为是全局变量嘛~)  
  6.  
  7. //线程入口函数的声明  
  8. DWORD WINAPI Func1Proc(LPVOID lpParameter);  
  9. DWORD WINAPI Func2Proc(LPVOID lpParameter);  
  10.  
  11. //主线程  
  12. int main()  
  13. {  
  14.     HANDLE hThread1;  
  15.     HANDLE hThread2;  
  16.  
  17.     /*第三个参数FALSE,表示事件对象初始化为无信号状态  
  18.      *第二个参数FASLE,表示线程申请事件对象之后(自动设置为无信号状态)  
  19.      */ 
  20.     hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);   
  21.       
  22.     hThread1=CreateThread(NULL,0,Func1Proc,NULL,0,NULL);  
  23.     hThread2=CreateThread(NULL,0,Func2Proc,NULL,0,NULL);  
  24.     CloseHandle(hThread1);  
  25.     CloseHandle(hThread2);  
  26.       
  27.     Sleep(4000);  
  28.       
  29.     return 0;  
  30. }  
  31.  
  32. //线程1的入口函数  
  33. DWORD WINAPI Func1Proc(LPVOID lpParameter)  
  34. {  
  35.     while(true)  
  36.     {  
  37.         WaitForSingleObject(hEvent,INFINITE);  
  38.         if(tickets>0)  
  39.         {  
  40.            
  41.            cout<<"thread1 sell tickets: "<<tickets--<<endl;  //tickets先输出,再减1  
  42.            SetEvent(hEvent);  
  43.         }  
  44.         else 
  45.         {  
  46.            break;  
  47.         }  
  48.           
  49.     }  
  50.       
  51.     return 0;  
  52. }  
  53. //线程2的入口函数  
  54. DWORD WINAPI Func2Proc(LPVOID lpParameter)  
  55. {  
  56.     while(true)  
  57.     {  
  58.         WaitForSingleObject(hEvent,INFINITE);  
  59.         if(tickets>0)  
  60.         {  
  61.              
  62.            cout<<"thread2 sell tickets: "<<tickets--<<endl;  //tickets先输出,再减1  
  63.            SetEvent(hEvent);  
  64.         }  
  65.         else 
  66.         {  
  67.            break;  
  68.         }  
  69.           
  70.     }  
  71.       
  72.     return 0;  
  73. }  

需要注意的是,CreateMutex函数的4个参数,说明如下:

HANDLE CreateEvent(  LPSECURITY_ATTRIBUTES ,   BOOL ,   BOOL ,   LPTSTR  );

第一个参数,同上;

第二个参数,表示申请到线程互斥对象后是否手动设置为无信号状态;

第三个参数,表示事件对象的初始化状态;(更正:代码中的注释行,应该是初始化为有信号状态

第四个参数,同上

 

代码3(初始化临界区对象函数):

 
  1. #include <windows.h>  
  2. #include <iostream.h>  
  3.  
  4. int tickets=100; //共享资源  
  5. CRITICAL_SECTION g_cs;   //共享对象(因为是全局变量嘛~)  
  6.  
  7. //线程入口函数的声明  
  8. DWORD WINAPI Func1Proc(LPVOID lpParameter);  
  9. DWORD WINAPI Func2Proc(LPVOID lpParameter);  
  10.  
  11. //主线程  
  12. int main()  
  13. {  
  14.     HANDLE hThread1;  
  15.     HANDLE hThread2;  
  16.  
  17.     /*似乎,临界区对象最方便,至少初始化的时候就只需要一个参数  
  18.      *  
  19.      */ 
  20.     InitializeCriticalSection(&g_cs);   
  21.       
  22.     hThread1=CreateThread(NULL,0,Func1Proc,NULL,0,NULL);  
  23.     hThread2=CreateThread(NULL,0,Func2Proc,NULL,0,NULL);  
  24.     CloseHandle(hThread1);  
  25.     CloseHandle(hThread2);  
  26.       
  27.     Sleep(4000);  
  28.     DeleteCriticalSection(&g_cs);  
  29.       
  30.     return 0;  
  31. }  
  32.  
  33. //线程1的入口函数  
  34. DWORD WINAPI Func1Proc(LPVOID lpParameter)  
  35. {  
  36.     while(true)  
  37.     {  
  38.         EnterCriticalSection(&g_cs);  
  39.  
  40.         if(tickets>0)  
  41.         {  
  42.            
  43.            cout<<"thread1 sell tickets: "<<tickets--<<endl;  //tickets先输出,再减1  
  44.         }  
  45.         else 
  46.         {  
  47.            break;  
  48.         }  
  49.  
  50.         LeaveCriticalSection(&g_cs);  
  51.           
  52.     }  
  53.       
  54.     return 0;  
  55. }  
  56. //线程2的入口函数  
  57. DWORD WINAPI Func2Proc(LPVOID lpParameter)  
  58. {  
  59.     while(true)  
  60.     {  
  61.         EnterCriticalSection(&g_cs);  
  62.  
  63.         if(tickets>0)  
  64.         {  
  65.              
  66.            cout<<"thread2 sell tickets: "<<tickets--<<endl;  //tickets先输出,再减1  
  67.         }  
  68.         else 
  69.         {  
  70.            break;  
  71.         }  
  72.  
  73.         LeaveCriticalSection(&g_cs);  
  74.           
  75.     }  
  76.       
  77.     return 0;  
  78. }  

 

 

我现在想说的是,如果综合起来看的话,对于第一种方法,需要通过WaitForSingleObject函数来申请互斥对象,一旦申请到后,(操作系统会将互斥对象设置为无信号状态),以防止其它线程申请。当售完票之后,该线程必须调用ReleaseMutex函数释放互斥对象,(操作系统又会将互斥对象设置为有信号状态)。这时,其它线程就可以申请互斥对象。如此循环,即可实现线程的协调。

对于第三种方法,原理一样,无非换成了EnterCriticalSection函数和LeaveCriticalSection函数。

而第二种方法,大致也是如此,只是换成了WaitForSingleObject函数和SetEvent函数。

但这几种线程协调的方法,肯定有不同之处(不然弄三个这玩意干嘛~),至于具体差别在哪儿,我也不是很清楚。因为书上也没有写清楚。