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

2014-11-24 00:57:51 · 作者: · 浏览: 0

一、简介

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递归锁

[cpp] view plaincopy 在CODE上查看代码片 派生到我的代码片
  1. #include #include
  2. #include
  3. int counter = 0;
  4. CRITICAL_SECTION g_cs;
  5. void doit(void* arg) {
  6. int i, val; for (i=0; i<5000; i++)
  7. { EnterCriticalSection(&g_cs);
  8. EnterCriticalSection(&g_cs);
  9. val = counter; printf("thread %d : %d\n", int(arg), val+1);
  10. counter = val + 1;
  11. LeaveCriticalSection(&g_cs); LeaveCriticalSection(&g_cs);
  12. } }
  13. int main(int argc, char*argv[])
  14. { InitializeCriticalSection(&g_cs);
  15. HANDLE hThread1 = CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)doit, (void*)1, 0, NULL);
  16. HANDLE hTrehad2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)doit, (void*)2, 0, NULL);
  17. WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hTrehad2, INFINITE);
  18. DeleteCriticalSection(&g_cs);
  19. return 0; }
    结果:加1次锁和2次锁,均可以正确的输出1~10000。

    2.2 pthread_mutex_t非递归锁

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
    1. #include #include
    2. #include
    3. int counter = 0;
    4. pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
    5. void* doit(void*) {
    6. int i, val; for (i=0; i<5000; i++)
    7. { pthread_mutex_lock(&g_mutex);
    8. pthread_mutex_lock(&g_mutex);
    9. val = counter; printf("%x: %d\n", pthread_self(), val+1);
    10. counter = val + 1;
    11. pthread_mutex_unlock(&g_mutex); pthread_mutex_unlock(&g_mutex);
    12. } }
    13. int main(int argc, char*argv[])
    14. { pthread_t tid1, tid2;
    15. pthread_create(&tid1, NULL, doit, NULL);
    16. pthread_create(&tid2, NULL, doit, NULL);
    17. pthread_join(tid1, NULL); pthread_join(tid2, NULL);
    18. return 0;
    19. }

      结果:加1次锁,可以正确的输出1~10000;加2次锁,死锁,不输出任何信息。

      2.3 pthread_mutex_t递归锁(PTHREAD_MUTEX_RECURSIVE)

      [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
      1. #include #include
      2. #include
      3. int counter = 0;
      4. pthread_mutex_t g_mutex;// = PTHREAD_MUTEX_INITIALIZER;
      5. void* doit(void*) {
      6. int i, val; for (i=0; i<5000; i++)
      7. { pthread_mutex_lock(&g_mutex);
      8. pthread_mutex_lock(&g_mutex);
      9. val = counter; printf("%x: %d\n", pthread_self(), val+1);
      10. counter = val + 1;
      11. pthread_mutex_unlock(&g_mutex); pthread_mutex_unlock(&g_mutex);
      12. } }
      13. int main(int argc, char*argv[])
      14. { //create recursive attribute
      15. pthread_mutexattr_t attr; pthread_mutexattr_init(&attr);
      16. //set recursive attribute
      17. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
      18. pthread_mutex_init(&g_mutex, &attr);
      19. pthread_t tid1, tid2;
      20. pthread_create(&tid1, NULL, doit, NULL); pthread_create(&tid2, NULL, doit, NULL);
      21. pthread_join(tid1, NULL);
      22. pthread_join(tid2, NULL);
      23. pthread_mutex_destroy(&g_mutex);
      24. //destroy recursive attribute
      25. pthread_mutexattr_destroy(&attr);
      26. return 0; }

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

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

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