线程同步--递归锁 非递归锁

一、简介

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.   
  5. int counter = 0;  
  6.   
  7. CRITICAL_SECTION g_cs;  
  8.   
  9. void doit(void* arg)  
  10. {  
  11.     int i, val;  
  12.     for (i=0; i<5000; i++)  
  13.     {  
  14.         EnterCriticalSection(&g_cs);  
  15.         EnterCriticalSection(&g_cs);  
  16.   
  17.         val = counter;  
  18.         printf("thread %d : %d\n"int(arg), val+1);  
  19.         counter = val + 1;  
  20.   
  21.         LeaveCriticalSection(&g_cs);  
  22.         LeaveCriticalSection(&g_cs);  
  23.     }  
  24. }  
  25.   
  26. int main(int argc, char*argv[])  
  27. {  
  28.     InitializeCriticalSection(&g_cs);  
  29.   
  30.     HANDLE hThread1 = CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)doit, (void*)1, 0, NULL);  
  31.     HANDLE hTrehad2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)doit, (void*)2, 0, NULL);  
  32.   
  33.     WaitForSingleObject(hThread1, INFINITE);  
  34.     WaitForSingleObject(hTrehad2, INFINITE);  
  35.   
  36.     DeleteCriticalSection(&g_cs);  
  37.   
  38.       
  39.     return 0;  
  40. }  

结果:加1次锁和2次锁,均可以正确的输出1~10000。

2.2 pthread_mutex_t非递归锁

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

结果:加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.   
  5. int counter = 0;  
  6.   
  7. pthread_mutex_t g_mutex;// = PTHREAD_MUTEX_INITIALIZER;  
  8.   
  9. void* doit(void*)  
  10. {  
  11.         int i, val;  
  12.         for (i=0; i<5000; i++)  
  13.         {  
  14.                 pthread_mutex_lock(&g_mutex);  
  15.                 pthread_mutex_lock(&g_mutex);  
  16.   
  17.                 val = counter;  
  18.                 printf("%x: %d\n", pthread_self(), val+1);  
  19.                 counter = val + 1;  
  20.   
  21.                 pthread_mutex_unlock(&g_mutex);  
  22.                 pthread_mutex_unlock(&g_mutex);  
  23.         }  
  24. }  
  25.   
  26. int main(int argc, char*argv[])  
  27. {  
  28.         //create recursive attribute  
  29.         pthread_mutexattr_t attr;  
  30.         pthread_mutexattr_init(&attr);  
  31.   
  32.         //set recursive attribute  
  33.         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);  
  34.   
  35.         pthread_mutex_init(&g_mutex, &attr);  
  36.   
  37.   
  38.         pthread_t tid1, tid2;  
  39.         pthread_create(&tid1, NULL, doit, NULL);  
  40.         pthread_create(&tid2, NULL, doit, NULL);  
  41.   
  42.         pthread_join(tid1, NULL);  
  43.         pthread_join(tid2, NULL);  
  44.   
  45.   
  46.         pthread_mutex_destroy(&g_mutex);  
  47.   
  48.         //destroy recursive attribute  
  49.         pthread_mutexattr_destroy(&attr);  
  50.   
  51.         return 0;  
  52. }  

结果:加1次锁和2次锁,均可以正确的输出1~10000。、

在线程同步中,使用锁是一种非常常见的做法,尽量使用非递归锁,避免使用递归锁!

非递归锁的逻辑清晰,在出现死锁的时候可以轻松DEBUG!仅凭着一点就需要使用非递归锁!

线程同步--递归锁 非递归锁,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。