歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux線程同步

Linux線程同步

日期:2017/3/1 9:19:06   编辑:Linux編程

一.概述

互斥量是線程同步的一種機制,用來保護多線程的共享資源。同一時刻,只允許一個線程對臨界區進行訪問。

互斥量的工作流程:創建一個互斥量,把這個互斥量的加鎖調用放在臨界區的開始位置,解鎖調用放到臨界區的結束位置。當內核優先把某個線程調度到臨界區的開始位置時,線程執行這個加鎖調用,並進入臨界區對資源進行操作。此時其他線程再被內核調度到這裡的時候,由於該互斥量已被加鎖狀態,得不到鎖會一直阻塞在這裡,導致其他線程不能進入臨界區,直到剛剛那個進入臨界區的線程離開臨界區並執行解鎖調用。

二.函數接口

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

Copyright © Linux教程網 All Rights Reserved