一、简介
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递归锁
#include <Windows.h> #include <iostream> #include <string> int counter = 0; CRITICAL_SECTION g_cs; void doit(void* arg) { int i, val; for (i=0; i<5000; i++) { EnterCriticalSection(&g_cs); EnterCriticalSection(&g_cs); val = counter; printf("thread %d : %d\n", int(arg), val+1); counter = val + 1; LeaveCriticalSection(&g_cs); LeaveCriticalSection(&g_cs); } } int main(int argc, char*argv[]) { InitializeCriticalSection(&g_cs); HANDLE hThread1 = CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)doit, (void*)1, 0, NULL); HANDLE hTrehad2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)doit, (void*)2, 0, NULL); WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hTrehad2, INFINITE); DeleteCriticalSection(&g_cs); return 0; }
结果:加1次锁和2次锁,均可以正确的输出1~10000。
2.2 pthread_mutex_t非递归锁
#include <stdio.h> #include <stdlib.h> #include <pthread.h> int counter = 0; pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; void* doit(void*) { int i, val; for (i=0; i<5000; i++) { pthread_mutex_lock(&g_mutex); pthread_mutex_lock(&g_mutex); val = counter; printf("%x: %d\n", pthread_self(), val+1); counter = val + 1; pthread_mutex_unlock(&g_mutex); pthread_mutex_unlock(&g_mutex); } } int main(int argc, char*argv[]) { pthread_t tid1, tid2; pthread_create(&tid1, NULL, doit, NULL); pthread_create(&tid2, NULL, doit, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0; }
结果:加1次锁,可以正确的输出1~10000;加2次锁,死锁,不输出任何信息。
2.3 pthread_mutex_t递归锁(PTHREAD_MUTEX_RECURSIVE)
#include <stdio.h> #include <stdlib.h> #include <pthread.h> int counter = 0; pthread_mutex_t g_mutex;// = PTHREAD_MUTEX_INITIALIZER; void* doit(void*) { int i, val; for (i=0; i<5000; i++) { pthread_mutex_lock(&g_mutex); pthread_mutex_lock(&g_mutex); val = counter; printf("%x: %d\n", pthread_self(), val+1); counter = val + 1; pthread_mutex_unlock(&g_mutex); pthread_mutex_unlock(&g_mutex); } } int main(int argc, char*argv[]) { //create recursive attribute pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); //set recursive attribute pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&g_mutex, &attr); pthread_t tid1, tid2; pthread_create(&tid1, NULL, doit, NULL); pthread_create(&tid2, NULL, doit, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_mutex_destroy(&g_mutex); //destroy recursive attribute pthread_mutexattr_destroy(&attr); return 0; }
结果:加1次锁和2次锁,均可以正确的输出1~10000。