一.概述
互斥量是線程同步的一種機制,用來保護多線程的共享資源。同一時刻,只允許一個線程對臨界區進行訪問。
互斥量的工作流程:創建一個互斥量,把這個互斥量的加鎖調用放在臨界區的開始位置,解鎖調用放到臨界區的結束位置。當內核優先把某個線程調度到臨界區的開始位置時,線程執行這個加鎖調用,並進入臨界區對資源進行操作。此時其他線程再被內核調度到這裡的時候,由於該互斥量已被加鎖狀態,得不到鎖會一直阻塞在這裡,導致其他線程不能進入臨界區,直到剛剛那個進入臨界區的線程離開臨界區並執行解鎖調用。
二.函數接口
1.初始化互斥量
互斥量是一個pthread_mutex_t類型的變量。
1.1:用宏常量初始化:
1 pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
1.2:用函數初始化:
1 #include <pthread.h> 2 3 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
mutex:互斥量結構指針
attr:互斥量的屬性結構指針
2.設置互斥量屬性
1 #include <pthread.h> 2 3 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
attr:互斥量的屬性結構指針
type:PTHREAD_MUTEX_NORMAL(默認屬性),PTHREAD_MUTEX_ERRORCHECK(會進行錯誤檢查,速度比較慢),PTHREAD_MUTEX_RECURSIVE(遞歸鎖)。對於遞歸鎖,同一個線程對一個遞歸鎖加鎖多次,會有一個鎖計數器,解鎖的時候也需要解鎖這個次數才能釋放該互斥量。
3.加鎖與解鎖
1 #include <pthread.h> 2 3 int pthread_mutex_lock(pthread_mutex_t *mutex); 4 int pthread_mutex_trylock(pthread_mutex_t *mutex); 5 int pthread_mutex_unlock(pthread_mutex_t *mutex);
參數都是互斥量指針。pthread_mutex_lock()得不到鎖會阻塞,int pthread_mutex_trylock()得不到鎖會立即返回,並返回EBUSY錯誤。
還有一個pthread_mutex_timedlock()會根據時間來等待加鎖,如果這段時間得不到鎖會返回ETIMEDOUT錯誤!
1 #include <pthread.h> 2 #include <time.h> 3 4 int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);
4.銷毀互斥量
1 #include <pthread.h> 2 3 int pthread_mutex_destroy(pthread_mutex_t *mutex);
mutex:創建的互斥量指針
三.簡單例子
寫個簡單的例子,主線程消費,子線程生產,並模擬使用過程中可能遇到的缺點
1 /**
2 * @file pthread_mutex.c
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <pthread.h>
10
11 /* 定義互斥量 */
12 pthread_mutex_t mtx;
13 /* 互斥量屬性 */
14 pthread_mutexattr_t mtx_attr;
15 /* 全局資源 */
16 int money;
17
18 void err_exit(const char *err_msg)
19 {
20 printf("error:%s\n", err_msg);
21 exit(1);
22 }
23
24 /* 線程函數 */
25 void *thread_fun(void *arg)
26 {
27 while (1)
28 {
29 /* 加鎖 */
30 pthread_mutex_lock(&mtx);
31
32 printf("子線程進入臨界區查看money\n");
33 if (money == 0)
34 {
35 money += 200;
36 printf("子線程:money = %d\n", money);
37 }
38
39 /* 解鎖 */
40 pthread_mutex_unlock(&mtx);
41
42 sleep(1);
43 }
44
45 return NULL;
46 }
47
48 int main(void)
49 {
50 pthread_t tid;
51
52 /* 初始化互斥量屬性 */
53 if (pthread_mutexattr_init(&mtx_attr) == -1)
54 err_exit("pthread_mutexattr_init()");
55
56 /* 設置互斥量屬性 */
57 if (pthread_mutexattr_settype(&mtx_attr, PTHREAD_MUTEX_NORMAL) == -1)
58 err_exit("pthread_mutexattr_settype()");
59
60 /* 初始化互斥量 */
61 if (pthread_mutex_init(&mtx, &mtx_attr) == -1)
62 err_exit("pthread_mutex_init()");
63
64 /* 創建一個線程 */
65 if (pthread_create(&tid, NULL, thread_fun, NULL)== -1)
66 err_exit("pthread_create()");
67
68 money = 1000;
69 while (1)
70 {
71 /* 加鎖 */
72 pthread_mutex_lock(&mtx);
73
74 if (money > 0)
75 {
76 money -= 100;
77 printf("主線程:money = %d\n", money);
78 }
79
80 /* 解鎖 */
81 pthread_mutex_unlock(&mtx);
82
83 sleep(1);
84 }
85
86 return 0;
87 }
主線程和子線程都對money的操作進行了互斥量保護。68行,初始化money是1000,主線程每次消耗100,子線程只有到money是0是才會生產。sleep(1)防止獨占cpu,也方便打印信息。編譯運行:
可以看到這裡有個非常浪費資源的問題:主線程消耗money的時候,子線程它不知道什麼時候才消耗完,每次內核調度到它時,它都進入臨界區加鎖互斥量,然後查看money,再解鎖。這無意義的操作,簡直是極大的浪費!有什麼辦法可以解決這個問題呢?它有一個好伙伴,叫條件變量。
四.死鎖
假設:當線程1獲取鎖1,再獲取鎖2後才能進入臨界區1,線程2獲取鎖2,再獲取鎖1才能進入臨界區2。某個時刻,線程1獲取了鎖1,再去獲取鎖2的時候發現鎖2已經被線程2鎖住了,而線程2獲取鎖2後,發現鎖1被線程1鎖住了。這樣2個線程誰也不讓誰,都進不了自己的臨界區,就產生了死鎖現象!一般遇到這種情況常見的解決辦法是:規定統一的加鎖順序。線程1和線程2都按照先鎖1,再鎖2。還一種就是使用pthread_mutex_trylock(),如果該函數返回EBUSY錯誤,就釋放這個線程的所有鎖,不過效率有點低。
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2016-01/127766p2.htm