1. 程式人生 > >執行緒同步--遞迴鎖 非遞迴鎖

執行緒同步--遞迴鎖 非遞迴鎖

一、簡介

1.1 程序/執行緒同步方法

      常見的程序/執行緒同步方法有互斥鎖(或稱互斥量Mutex)、讀寫鎖(rdlock)、條件變數(cond)、訊號量(Semophore)等。

      在windows系統中,臨界區(Critical Section)和事件物件(Event)也是常用的同步方法。

1.2 遞迴鎖/非遞迴鎖

      Mutex可以分為遞迴鎖(recursive mutex)和非遞迴鎖(non-recursive mutex)。 遞迴鎖也叫可重入鎖(reentrant mutex),非遞迴鎖也叫不可重入鎖(non-reentrant mutex)。

      二者唯一的區別

是:

            同一個執行緒可以多次獲取同一個遞迴鎖,不會產生死鎖。

            如果一個執行緒多次獲取同一個非遞迴鎖,則會產生死鎖。

      Windows下的Mutex和Critical Section是可遞迴的。 

      Linux下的pthread_mutex_t鎖是預設是非遞迴的。可以通過設定PTHREAD_MUTEX_RECURSIVE屬性,將pthread_mutex_t鎖設定為遞迴鎖。

二、程式碼

2.1 Critical Section遞迴鎖

  1. #include <Windows.h>
  2. #include <iostream>
  3. #include <string>
  4. int counter = 0;  
  5. CRITICAL_SECTION g_cs;  
  6. void doit(void* arg)  
  7. {  
  8.     int i, val;  
  9.     for (i=0; i<5000; i++)  
  10.     {  
  11.         EnterCriticalSection(&g_cs);  
  12.         EnterCriticalSection(&g_cs);  
  13.         val = counter;  
  14.         printf("thread %d : %d\n"int(arg), val+1);  
  15.         counter = val + 1;  
  16.         LeaveCriticalSection(&g_cs);  
  17.         LeaveCriticalSection(&g_cs);  
  18.     }  
  19. }  
  20. int main(int argc, char*argv[])  
  21. {  
  22.     InitializeCriticalSection(&g_cs);  
  23.     HANDLE hThread1 = CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)doit, (void*)1, 0, NULL);  
  24.     HANDLE hTrehad2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)doit, (void*)2, 0, NULL);  
  25.     WaitForSingleObject(hThread1, INFINITE);  
  26.     WaitForSingleObject(hTrehad2, INFINITE);  
  27.     DeleteCriticalSection(&g_cs);  
  28.     return 0;  
  29. }  

結果:加1次鎖和2次鎖,均可以正確的輸出1~10000。

2.2 pthread_mutex_t非遞迴鎖

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <pthread.h>
  4. int counter = 0;  
  5. pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;  
  6. void* doit(void*)  
  7. {  
  8.         int i, val;  
  9.         for (i=0; i<5000; i++)  
  10.         {  
  11.                 pthread_mutex_lock(&g_mutex);  
  12.                 pthread_mutex_lock(&g_mutex);  
  13.                 val = counter;  
  14.                 printf("%x: %d\n", pthread_self(), val+1);  
  15.                 counter = val + 1;  
  16.                 pthread_mutex_unlock(&g_mutex);  
  17.                 pthread_mutex_unlock(&g_mutex);        
  18.         }  
  19. }  
  20. int main(int argc, char*argv[])  
  21. {  
  22.         pthread_t tid1, tid2;  
  23.         pthread_create(&tid1, NULL, doit, NULL);  
  24.         pthread_create(&tid2, NULL, doit, NULL);  
  25.         pthread_join(tid1, NULL);  
  26.         pthread_join(tid2, NULL);  
  27.         return 0;  
  28. }  

結果:加1次鎖,可以正確的輸出1~10000;加2次鎖,死鎖,不輸出任何資訊。

2.3 pthread_mutex_t遞迴鎖(PTHREAD_MUTEX_RECURSIVE)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <pthread.h>
  4. int counter = 0;  
  5. pthread_mutex_t g_mutex;// = PTHREAD_MUTEX_INITIALIZER;
  6. void* doit(void*)  
  7. {  
  8.         int i, val;  
  9.         for (i=0; i<5000; i++)  
  10.         {  
  11.                 pthread_mutex_lock(&g_mutex);  
  12.                 pthread_mutex_lock(&g_mutex);  
  13.                 val = counter;  
  14.                 printf("%x: %d\n", pthread_self(), val+1);  
  15.                 counter = val + 1;  
  16.                 pthread_mutex_unlock(&g_mutex);  
  17.                 pthread_mutex_unlock(&g_mutex);  
  18.         }  
  19. }  
  20. int main(int argc, char*argv[])  
  21. {  
  22.         //create recursive attribute
  23.         pthread_mutexattr_t attr;  
  24.         pthread_mutexattr_init(&attr);  
  25.         //set recursive attribute
  26.         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);  
  27.         pthread_mutex_init(&g_mutex, &attr);  
  28.         pthread_t tid1, tid2;  
  29.         pthread_create(&tid1, NULL, doit, NULL);  
  30.         pthread_create(&tid2, NULL, doit, NULL);  
  31.         pthread_join(tid1, NULL);  
  32.         pthread_join(tid2, NULL);  
  33.         pthread_mutex_destroy(&g_mutex);  
  34.         //destroy recursive attribute
  35.         pthread_mutexattr_destroy(&attr);  
  36.         return 0;  
  37. }  

結果:加1次鎖和2次鎖,均可以正確的輸出1~10000。、

線上程同步中,使用鎖是一種非常常見的做法,儘量使用非遞迴鎖,避免使用遞迴鎖!

非遞迴鎖的邏輯清晰,在出現死鎖的時候可以輕鬆DEBUG!僅憑著一點就需要使用非遞迴鎖!