http://blog.csdn.net/jnu_simba/article/details/9123603
一、posix 信號量
信號量的概念參見這里。前面也講過system v 信號量,現在來說說posix 信號量。
system v 信號量只能用于進程間同步,而posix 信號量除了可以進程間同步,還可以線程間同步。system v 信號量每次PV操作可以是N,但Posix 信號量每次PV只能是1。除此之外,posix 信號量還有命名和匿名之分(man 7 sem_overview):
1、命名信號量
名字以/somename 形式分辨,只能有一個/ ,且總長不能超過NAME_MAX - 4(一般是251)。
需要用sem_open 函數創建或打開,PV操作分別是sem_wait 和 sem_post,可以使用sem_close 關閉,刪除用sem_unlink。
命名信號量用于不共享內存的進程間同步(內核實現),類似system v 信號量。
2、匿名信號量
存放在一塊共享內存中,如果是線程共享,這塊區域可以是全局變量;如果是進程共享,可以是system v 共享內存(shmget 創建,shmat 映射),也可以是 posix 共享內存(shm_open 創建,mmap 映射)。
匿名信號量必須用sem_init 初始化,sem_init 函數其中一個參數pshared決定了線程共享還是進程共享,也可以用sem_post 和sem_wait 進行操作,在共享內存釋放前,匿名信號量要先用sem_destroy 銷毀。
有關這些函數的具體參數可以man 一下。
二、互斥鎖
對于多線程的程序,訪問沖突的問題是很普遍的,解決的辦法是引入互斥鎖(Mutex,MutualExclusive Lock),獲得鎖的線程可以完成“讀-修改-寫”的操作,然后釋放鎖給其它線程,沒有獲得鎖的線程只能等待而不能訪問共享數據,這樣“讀-修改-寫”三步操作組成一個原子操作,要么都執行,要么都不執行,不會執行到中間被打斷,也不會在其它處理器上并行做這個操作。
Mutex用pthread_mutex_t類型的變量表示,pthread_mutex_init函數對Mutex做初始化,參數attr設定Mutex的屬性,如果attr為NULL則表示缺省屬性,具體看結構體:有人建議開發中總是設置 PTHREAD_MUTEX_RECURSIVE 屬性,避免死鎖。
// 互斥量屬性: 同一線程可多次加鎖
pthread_mutexattr_t m_attr;
pthread_mutexattr_init(&m_attr);
pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE);
1 2 3 4 5 6 7 8 9 10 | struct?pthread_mutexattr_t { ????enum?lock_type????//?使用pthread_mutexattr_settype來更改 ????{ ?????????PTHREAD_MUTEX_TIMED_NP?[default]//當一個線程加鎖后,其余請求鎖的線程形成等待隊列,在解鎖后按優先級獲得鎖。 ?????????PTHREAD_MUTEX_ADAPTIVE_NP???????//?動作最簡單的鎖類型,解鎖后所有線程重新競爭。 ?????????PTHREAD_MUTEX_RECURSIVE_NP??????//?允許同一線程對同一鎖成功獲得多次。當然也要解鎖多次。其余線程在解鎖時重新競爭。 ?????????PTHREAD_MUTEX_ERRORCHECK_NP?????//?若同一線程請求同一鎖,返回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP動作相同。 ????}?type; }?attr; |
用pthread_mutex_init函數初始化的Mutex可以用pthread_mutex_destroy銷毀。如果Mutex變量是靜態分配的(全局變量或static變量),也可以用宏定義PTHREAD_MUTEX_INITIALIZER來初始化,相當于用pthread_mutex_init初始化并且attr參數為NULL。
一個線程可以調用pthread_mutex_lock獲得Mutex,如果這時另一個線程已經調用pthread_mutex_lock獲得了該Mutex,則當前線程需要掛起等待,直到另一個線程調用pthread_mutex_unlock釋放Mutex,當前線程被喚醒,才能獲得該Mutex并繼續執行。
如果一個線程既想獲得鎖,又不想掛起等待,可以調用pthread_mutex_trylock,如果Mutex已經被另一個線程獲得,這個函數會失敗返回EBUSY,而不會使線程掛起等待。
上面的具體函數可以man 一下。
三、生產者消費者問題
生產者消費者問題概念參見這里。下面使用posix 信號量和互斥鎖一起來演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | #include?<unistd.h> #include?<sys/types.h> #include?<pthread.h> #include?<semaphore.h> #include?<stdlib.h> #include?<stdio.h> #include?<errno.h> #include?<string.h> #define?ERR_EXIT(m)?\ ????????do?\ ????????{?\ ????????????????perror(m);?\ ????????????????exit(EXIT_FAILURE);?\ ????????}?while(0) #define?CONSUMERS_COUNT?1 #define?PRODUCERS_COUNT?1 #define?BUFFSIZE?10 int?g_buffer[BUFFSIZE]; unsigned?short?in?=?0; unsigned?short?out?=?0; unsigned?short?produce_id?=?0; unsigned?short?consume_id?=?0; sem_t?g_sem_full; sem_t?g_sem_empty; pthread_mutex_t?g_mutex; pthread_t?g_thread[CONSUMERS_COUNT?+?PRODUCERS_COUNT]; void?*consume(void?*arg) { ????int?i; ????int?num?=?(int)arg; ????while?(1) ????{ ????????printf("%d?wait?buffer?not?empty\n",?num); ????????sem_wait(&g_sem_empty); ????????pthread_mutex_lock(&g_mutex); ????????for?(i?=?0;?i?<?BUFFSIZE;?i++) ????????{ ????????????printf("%02d?",?i); ????????????if?(g_buffer[i]?==?-1) ????????????????printf("%s",?"null"); ????????????else ????????????????printf("%d",?g_buffer[i]); ????????????if?(i?==?out) ????????????????printf("\t<--consume"); ????????????printf("\n"); ????????} ????????consume_id?=?g_buffer[out]; ????????printf("%d?begin?consume?product?%d\n",?num,?consume_id); ????????g_buffer[out]?=?-1; ????????out?=?(out?+?1)?%?BUFFSIZE; ????????printf("%d?end?consume?product?%d\n",?num,?consume_id); ????????pthread_mutex_unlock(&g_mutex); ????????sem_post(&g_sem_full); ????????sleep(1); ????} ????return?NULL; } void?*produce(void?*arg) { ????int?num?=?(int)arg; ????int?i; ????while?(1) ????{ ????????printf("%d?wait?buffer?not?full\n",?num); ????????sem_wait(&g_sem_full); ????????pthread_mutex_lock(&g_mutex); ????????for?(i?=?0;?i?<?BUFFSIZE;?i++) ????????{ ????????????printf("%02d?",?i); ????????????if?(g_buffer[i]?==?-1) ????????????????printf("%s",?"null"); ????????????else ????????????????printf("%d",?g_buffer[i]); ????????????if?(i?==?in) ????????????????printf("\t<--produce"); ????????????printf("\n"); ????????} ????????printf("%d?begin?produce?product?%d\n",?num,?produce_id); ????????g_buffer[in]?=?produce_id; ????????in?=?(in?+?1)?%?BUFFSIZE; ????????printf("%d?end?produce?product?%d\n",?num,?produce_id++); ????????pthread_mutex_unlock(&g_mutex); ????????sem_post(&g_sem_empty); ????????sleep(5); ????} ????return?NULL; } int?main(void) { ????int?i; ????for?(i?=?0;?i?<?BUFFSIZE;?i++) ????????g_buffer[i]?=?-1; ????sem_init(&g_sem_full,?0,?BUFFSIZE); ????sem_init(&g_sem_empty,?0,?0); ????pthread_mutex_init(&g_mutex,?NULL); ????for?(i?=?0;?i?<?CONSUMERS_COUNT;?i++) ????????pthread_create(&g_thread[i],?NULL,?consume,?(void?*)i); ????for?(i?=?0;?i?<?PRODUCERS_COUNT;?i++) ????????pthread_create(&g_thread[CONSUMERS_COUNT?+?i],?NULL,?produce,?(void?*)i); ????for?(i?=?0;?i?<?CONSUMERS_COUNT?+?PRODUCERS_COUNT;?i++) ????????pthread_join(g_thread[i],?NULL); ????sem_destroy(&g_sem_full); ????sem_destroy(&g_sem_empty); ????pthread_mutex_destroy(&g_mutex); ????return?0; } |
與這里的程序相比,程序邏輯沒太大變化,只是用pthread_mutex_lock 替代了 sem_mutex,其次這里是演示線程間同步,現在上述程序生產者消費者各一個線程,但生產者睡眠時間是消費者的5倍,故消費者會經常阻塞在sem_wait(&g_sem_empty) 上面,因為緩沖區經常為空,可以將PRODUCTORS_COUNT 改成5,即有5個生產者線程和1個消費者線程,而且生產者睡眠時間還是消費者的5倍,從動態輸出可以看出,基本上就動態平衡了,即5個生產者一下子生產了5份東西,消費者1s消費1份,剛好在生產者繼續生產前消費完。
四、自旋鎖和讀寫鎖簡介
(一)、自旋鎖
自旋鎖類似于互斥鎖,它的性能比互斥鎖更高。
自旋鎖與互斥鎖很重要的一個區別在于,線程在申請自旋鎖的時候,線程不會被掛起,它處于忙等待的狀態,一般用于等待時間比較短的情形。
pthread_spin_init
pthread_spin_destroy
pthread_spin_lock
pthread_spin_unlock
(二)、讀寫鎖
1、只要沒有線程持有給定的讀寫鎖用于寫,那么任意數目的線程可以持有讀寫鎖用于讀
2、僅當沒有線程持有某個給定的讀寫鎖用于讀或用于寫時,才能分配讀寫鎖用于寫
3、讀寫鎖用于讀稱為共享鎖,讀寫鎖用于寫稱為排它鎖
pthread_rwlock_init
pthread_rwlock_destroy
int pthread_rwlock_rdlock
int pthread_rwlock_wrlock
int pthread_rwlock_unlock
更多有關linux中的鎖問題可以參考這篇文章 :《透過Linux內核看無鎖編程》
http://www.ibm.com/developerworks/cn/linux/l-cn-lockfree/
參考:
《linux c 編程一站式學習》
《UNP》