成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux多線程同步機(jī)制-互斥鎖(mutex)

系統(tǒng) Linux
在使用互斥鎖時(shí),需要注意正確的加鎖和解鎖順序,避免死鎖等問題。同時(shí),過度使用互斥鎖可能導(dǎo)致性能下降,因?yàn)榫€程可能會(huì)因等待鎖而阻塞。因此,在設(shè)計(jì)多線程程序時(shí),需要仔細(xì)考慮互斥鎖的使用位置和時(shí)機(jī),以達(dá)到最佳的性能和正確性平衡。

引言

在Linux多線程編程中,互斥鎖(Mutex)是一種非常重要的同步機(jī)制,用于控制對(duì)共享資源的訪問,確保在任意時(shí)刻只有一個(gè)線程可以訪問特定的資源或代碼段,即臨界區(qū)。互斥鎖的主要用途是防止多個(gè)線程同時(shí)訪問共享資源,從而避免競(jìng)爭(zhēng)條件和數(shù)據(jù)不一致的問題。

互斥鎖的工作原理相對(duì)簡(jiǎn)單,它通過鎖定和解鎖操作來控制對(duì)共享資源的訪問。當(dāng)一個(gè)線程需要訪問共享資源時(shí),它首先嘗試鎖定互斥鎖。如果互斥鎖已經(jīng)被其他線程鎖定,請(qǐng)求線程將被阻塞,直到互斥鎖被解鎖。互斥鎖的鎖定和解鎖操作必須是成對(duì)出現(xiàn)的,以確保對(duì)共享資源的正確訪問。

一、互斥鎖原型

在 Linux 中,互斥鎖通常通過 POSIX 線程庫(kù)(pthread)來實(shí)現(xiàn)。pthread 庫(kù)提供了一系列的函數(shù)來創(chuàng)建、初始化、鎖定、解鎖和銷毀互斥鎖,如pthread_mutex_init()、pthread_mutex_lock()、pthread_mutex_unlock()和pthread_mutex_destroy()等。互斥鎖的初始化是通過pthread_mutex_init()函數(shù)完成的,該函數(shù)會(huì)分配必要的資源來創(chuàng)建一個(gè)互斥鎖,并將其初始化為未鎖定狀態(tài)。

pthread_mutex_t 是 POSIX 線程庫(kù)中用于互斥鎖的數(shù)據(jù)類型。以下是一些與 pthread_mutex_t 相關(guān)的函數(shù)原型:

初始化互斥鎖:

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

mutex:指向 pthread_mutex_t 結(jié)構(gòu)的指針,用于初始化互斥鎖。

attr:指向 pthread_mutexattr_t 結(jié)構(gòu)的指針,包含互斥鎖的屬性。如果為 NULL,則使用默認(rèn)屬性。

銷毀互斥鎖:

int pthread_mutex_destroy(pthread_mutex_t *mutex);
  • mutex:指向已經(jīng)初始化的 pthread_mutex_t 結(jié)構(gòu)的指針。銷毀互斥鎖前,確保互斥鎖沒有被鎖定。

鎖定互斥鎖:

int pthread_mutex_lock(pthread_mutex_t *mutex);
  • mutex:指向已經(jīng)初始化的 pthread_mutex_t 結(jié)構(gòu)的指針。如果互斥鎖已經(jīng)被其他線程鎖定,則調(diào)用線程將被阻塞,直到互斥鎖被解鎖。

嘗試鎖定互斥鎖(非阻塞):

int pthread_mutex_trylock(pthread_mutex_t *mutex);
  • mutex:指向已經(jīng)初始化的 pthread_mutex_t 結(jié)構(gòu)的指針。如果互斥鎖已經(jīng)被鎖定,則立即返回錯(cuò)誤碼 EBUSY,而不是等待互斥鎖變?yōu)榭捎谩?/li>

定時(shí)鎖定互斥鎖(可指定超時(shí)時(shí)間):

int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
  • mutex:指向已經(jīng)初始化的 pthread_mutex_t 結(jié)構(gòu)的指針。
  • abstime:指向 timespec 結(jié)構(gòu)的指針,包含超時(shí)時(shí)間。如果超時(shí)時(shí)間到達(dá),互斥鎖仍未解鎖,則返回 ETIMEDOUT。

解鎖互斥鎖:

int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • mutex:指向已經(jīng)初始化且當(dāng)前被調(diào)用線程鎖定的 pthread_mutex_t 結(jié)構(gòu)的指針。調(diào)用此函數(shù)將釋放互斥鎖,允許其他線程鎖定它。

注意:互斥鎖的初始化方式主要有兩種:靜態(tài)初始化和動(dòng)態(tài)初始化。

  • 靜態(tài)初始化: 使用宏 PTHREAD_MUTEX_INITIALIZER 可以在聲明互斥鎖變量時(shí)直接初始化。這種方式是編譯時(shí)初始化,無需調(diào)用初始化函數(shù)。例如:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

這種方式適用于靜態(tài)分配的互斥鎖,即在程序的整個(gè)生命周期內(nèi)都存在的鎖。

  • 動(dòng)態(tài)初始化: 使用 pthread_mutex_init() 函數(shù)可以在程序運(yùn)行時(shí)初始化互斥鎖。這種方式需要顯式調(diào)用函數(shù)進(jìn)行初始化和銷毀,適用于需要?jiǎng)討B(tài)創(chuàng)建和銷毀的互斥鎖。例如:

pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t));pthread_mutex_init(mutex, NULL); // NULL 表示使用默認(rèn)屬性

使用動(dòng)態(tài)初始化時(shí),需要在不再需要互斥鎖時(shí)調(diào)用 pthread_mutex_destroy() 來銷毀它,并釋放分配的內(nèi)存。

互斥鎖的屬性可以在初始化時(shí)指定,如果不設(shè)置屬性(即使用 NULL 或默認(rèn)屬性),則使用系統(tǒng)默認(rèn)的互斥鎖屬性。在Linux系統(tǒng)中,屬性可以用來定義不同類型的互斥鎖,例如:

  • 普通互斥鎖(PTHREAD_MUTEX_TIMED_NP)
  • 遞歸互斥鎖(PTHREAD_MUTEX_RECURSIVE_NP)
  • 錯(cuò)誤檢查互斥鎖(PTHREAD_MUTEX_ERRORCHECK_NP)
  • 適應(yīng)性互斥鎖(PTHREAD_MUTEX_ADAPTIVE_NP)

正確初始化互斥鎖對(duì)于避免潛在的同步問題是非常重要的。使用靜態(tài)初始化可以簡(jiǎn)化代碼并減少出錯(cuò)機(jī)會(huì),而動(dòng)態(tài)初始化提供了更大的靈活性。在實(shí)際編程中,應(yīng)根據(jù)具體需求選擇適合的初始化方式。

這些函數(shù)是 POSIX 線程庫(kù)中用于線程同步的基礎(chǔ),通過它們可以安全地在多線程程序中控制對(duì)共享資源的訪問。

三、互斥鎖特性

互斥鎖是一種簡(jiǎn)單的加鎖的方法來控制對(duì)共享資源的訪問,互斥鎖只有兩種狀態(tài),即上鎖( lock )和解鎖(unlock),如果互斥量已經(jīng)上鎖,調(diào)用線程會(huì)阻塞,直到互斥量被解鎖。在完成了對(duì)共享資源的訪問后,要對(duì)互斥量進(jìn)行解鎖。

互斥鎖特點(diǎn)如下:

  1. 原子性:把一個(gè)互斥量鎖定為一個(gè)原子操作,操作系統(tǒng)保證了如果一個(gè)線程鎖定了一個(gè)互斥量,沒有其他線程在同一時(shí)間可以再成功鎖定這個(gè)互斥量;
  2. 唯一性:如果一個(gè)線程鎖定了一個(gè)互斥量,在它解除鎖定之前,沒有其他線程可以鎖定這個(gè)互斥量;
  3. 非忙等待:如果一個(gè)線程已經(jīng)鎖定了一個(gè)互斥量,第二個(gè)線程又試圖去鎖定這個(gè)互斥量,則第二個(gè)線程將被掛起(不占用任何 cpu 資源),直到第一個(gè)線程解除對(duì)這個(gè)互斥量的鎖定為止,第二個(gè)線程則被喚醒并繼續(xù)執(zhí)行,同時(shí)鎖定這個(gè)互斥量。

四、互斥鎖加鎖阻塞

互斥鎖是一種【獨(dú)占鎖】,比如當(dāng)線程 A 加鎖成功后,此時(shí)互斥鎖已經(jīng)被線程 A 獨(dú)占了,只要線程 A 沒有釋放手中的鎖,線程 B 加鎖就會(huì)失敗,于是就會(huì)釋放 CPU 讓給其他線程,既然線程 B 釋放掉了 CPU,自然線程 B 加鎖的代碼就會(huì)被阻塞。

對(duì)于互斥鎖加鎖失敗而阻塞的現(xiàn)象,是由操作系統(tǒng)內(nèi)核實(shí)現(xiàn)的。當(dāng)加鎖失敗時(shí),內(nèi)核會(huì)將線程置為【睡眠】狀態(tài),等到鎖被釋放后,內(nèi)核會(huì)在合適的時(shí)機(jī)喚醒線程,當(dāng)這個(gè)線程成功獲取到鎖后,于是就可以繼續(xù)執(zhí)行。如下圖:

圖片圖片

所以,互斥鎖加鎖失敗時(shí),會(huì)從用戶態(tài)陷入到內(nèi)核態(tài),讓內(nèi)核幫我們切換線程,雖然簡(jiǎn)化了使用鎖的難度,但是存在一定的性能開銷成本。

那這個(gè)開銷成本是什么呢?會(huì)有兩次線程上下文切換的成本:

  • 當(dāng)線程加鎖失敗時(shí),內(nèi)核會(huì)把線程的狀態(tài)從【運(yùn)行】狀態(tài)設(shè)置為【睡眠】狀態(tài),然后把 CPU 切換給其他線程運(yùn)行;
  • 接著,當(dāng)鎖被釋放時(shí),之前【睡眠】狀態(tài)的線程會(huì)變?yōu)椤揪途w】狀態(tài),然后內(nèi)核會(huì)在合適的時(shí)間,把 CPU 切換給該線程運(yùn)行。

線程的上下文切換的是什么?當(dāng)兩個(gè)線程是屬于同一個(gè)進(jìn)程,因?yàn)樘摂M內(nèi)存是共享的,所以在切換時(shí),虛擬內(nèi)存這些資源就保持不動(dòng),只需要切換線程的私有數(shù)據(jù)、寄存器等不共享的數(shù)據(jù)。

上下切換的耗時(shí)有大佬統(tǒng)計(jì)過,大概在幾十納秒到幾微秒之間,如果你鎖住的代碼執(zhí)行時(shí)間比較短,那可能上下文切換的時(shí)間都比你鎖住的代碼執(zhí)行時(shí)間還要長(zhǎng)。

所以,如果你能確定被鎖住的代碼執(zhí)行時(shí)間很短,就不應(yīng)該用互斥鎖,而應(yīng)該選用自旋鎖,否則使用互斥鎖。

五、互斥鎖死鎖

互斥鎖導(dǎo)致的死鎖是多線程編程中的一個(gè)常見問題。死鎖發(fā)生時(shí),兩個(gè)或多個(gè)線程被無限期地阻塞,因?yàn)樗鼈冊(cè)诘却龑?duì)方釋放鎖。以下是一些關(guān)于互斥鎖死鎖的信息:

  1. 死鎖的條件:死鎖通常發(fā)生在以下四個(gè)條件同時(shí)滿足時(shí):

互斥:資源不能被多個(gè)線程同時(shí)訪問。

占有和等待:一個(gè)線程持有至少一個(gè)鎖,并且等待獲取其他線程持有的鎖。

不可剝奪:已經(jīng)獲得的鎖不能被其他線程強(qiáng)行剝奪,只能由持有它的線程釋放。

循環(huán)等待:存在一個(gè)線程持有鎖的循環(huán)鏈,每個(gè)線程都在等待下一個(gè)線程持有的鎖。

  1. 死鎖的避免:可以通過以下幾種策略來避免死鎖:

固定順序加鎖:總是以相同的順序獲取多個(gè)鎖。

超時(shí)嘗試加鎖:使用 pthread_mutex_trylock() 或其他帶有超時(shí)功能的加鎖方法。

一次性獲取所有鎖:在開始訪問共享資源前,先獲取所有需要的鎖。

使用死鎖檢測(cè)算法:定期檢測(cè)死鎖情況,并采取措施解決。

  1. 死鎖的解除:如果檢測(cè)到死鎖,可以采取以下措施:

剝奪資源:從其他線程剝奪資源,賦予死鎖線程。

撤銷進(jìn)程:終止一個(gè)或多個(gè)線程或進(jìn)程,打破死鎖狀態(tài)。

資源重分配:重新分配資源,以打破循環(huán)等待條件。

  1. 避免嵌套鎖:盡量減少鎖的嵌套使用,如果必須嵌套使用,確保內(nèi)層鎖總是不同類型的或者使用不同的加鎖順序。
  2. 鎖的分級(jí)管理:將鎖分級(jí),高級(jí)別的鎖可以包含多個(gè)低級(jí)別的鎖,確保在請(qǐng)求高級(jí)別鎖時(shí),已經(jīng)持有所有需要的低級(jí)別鎖。

通過采取這些措施,可以降低死鎖發(fā)生的風(fēng)險(xiǎn),并提高多線程程序的穩(wěn)定性和可靠性。

六、互斥鎖實(shí)戰(zhàn)

1.互斥鎖加解鎖

以下是一個(gè)簡(jiǎn)單的示例代碼,展示了如何在多線程環(huán)境中使用 pthread_mutex_t 來同步對(duì)同一個(gè)文件的讀寫操作:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define NUM_THREADS 5 // 定義宏來設(shè)置線程數(shù)量

pthread_mutex_t file_mutex = PTHREAD_MUTEX_INITIALIZER; // 全局互斥鎖

const char *data_to_write = "Thread data\n";

void perform_file_write(const char *filename) {
    FILE *fp = fopen(filename, "a");
    if (fp == NULL) {
        perror("Error opening file for write");
        return;
    }

    pthread_mutex_lock(&file_mutex); // 加鎖
    fputs(data_to_write, fp);
    pthread_mutex_unlock(&file_mutex); // 解鎖
    fclose(fp);
}

void perform_file_read(const char *filename) {
    FILE *fp = fopen(filename, "r");
    if (fp == NULL) {
        perror("Error opening file for read");
        return;
    }

    char buffer[256];
    pthread_mutex_lock(&file_mutex); // 加鎖
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer); // 打印讀取的數(shù)據(jù)
    }
    pthread_mutex_unlock(&file_mutex); // 解鎖
    fclose(fp);
}

void *thread_function(void *arg) {
    int is_write = *(int *)arg; // 根據(jù)傳入的參數(shù)決定操作類型
    const char *filename = "example.txt"; // 要操作的文件名

    if (is_write) {
        perform_file_write(filename);
    } else {
        perform_file_read(filename);
    }
    return NULL;
}

int main() {
    int *args = malloc(NUM_THREADS * sizeof(int)); // 動(dòng)態(tài)分配參數(shù)數(shù)組
    if (args == NULL) {
        perror("Failed to allocate memory for args");
        return 1;
    }

    pthread_t threads[NUM_THREADS];
    for (int i = 0; i < NUM_THREADS; ++i) {
        args[i] = (i == 0); // 第一個(gè)線程執(zhí)行寫操作,其余執(zhí)行讀操作
        if (pthread_create(&threads[i], NULL, thread_function, &args[i]) != 0) {
            perror("Failed to create thread");
            free(args);
            return 1;
        }
    }

    for (int i = 0; i < NUM_THREADS; ++i) {
        pthread_join(threads[i], NULL);
    }

    free(args); // 釋放參數(shù)數(shù)組
    // 靜態(tài)初始化的互斥鎖不需要顯式銷毀
    return 0;
}

在這個(gè)示例中,創(chuàng)建了一個(gè)互斥鎖 file_mutex 來同步對(duì)文件 example.txt 的訪問。使用 PTHREAD_MUTEX_INITIALIZER 直接初始化互斥鎖,無需再調(diào)用pthread_mutex_init 函數(shù)了。我們定義了 perform_file_read 和 perform_file_write 函數(shù)來執(zhí)行實(shí)際的文件讀寫操作,并在這些操作前后使用 pthread_mutex_lock 和 pthread_mutex_unlock 來確保每次只有一個(gè)線程可以訪問文件。在 main 函數(shù)中,創(chuàng)建了一個(gè)線程數(shù)組,并設(shè)置第一個(gè)線程執(zhí)行寫操作,其余線程執(zhí)行讀操作。使用 perror 來打印出創(chuàng)建線程或文件操作失敗時(shí)的錯(cuò)誤信息。最后,我們?cè)谥骱瘮?shù)中等待所有線程完成,并且由于使用了靜態(tài)初始化互斥鎖,我們不需要調(diào)用 pthread_mutex_destroy 來銷毀互斥鎖。

程序運(yùn)行結(jié)果如下:

[root@localhost multi_pthread_file]# ./multi_pthread_file_rw
Thread data
Thread data
Thread data
Thread data
[root@localhost multi_pthread_file]# cat example.txt
Thread data

通過程序運(yùn)行結(jié)果可知,我們通過給寫操作和讀操作加互斥鎖,成功實(shí)現(xiàn)我們預(yù)期的目標(biāo),即寫一次數(shù)據(jù),讀四次數(shù)據(jù)。

2.互斥鎖死鎖

以下是一個(gè) C 語言中可能導(dǎo)致死鎖的互斥鎖代碼示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t mutex1;
pthread_mutex_t mutex2;

void *thread1Function(void *arg) {
    pthread_mutex_lock(&mutex1);
    printf("Thread 1 acquired mutex1\n");

    sleep(1);

    pthread_mutex_lock(&mutex2);
    printf("Thread 1 acquired mutex2\n");

    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);

    return NULL;
}

void *thread2Function(void *arg) {
    pthread_mutex_lock(&mutex2);
    printf("Thread 2 acquired mutex2\n");

    sleep(1);

    pthread_mutex_lock(&mutex1);
    printf("Thread 2 acquired mutex1\n");

    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex2);

    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    pthread_mutex_init(&mutex1, NULL);
    pthread_mutex_init(&mutex2, NULL);

    pthread_create(&thread1, NULL, thread1Function, NULL);
    pthread_create(&thread2, NULL, thread2Function, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);

    return 0;
}

在這個(gè)示例中,thread1 先獲取 mutex1 然后嘗試獲取 mutex2,而 thread2 先獲取 mutex2 然后嘗試獲取 mutex1,可能會(huì)導(dǎo)致兩個(gè)線程相互等待對(duì)方釋放鎖,從而造成死鎖。

【拓展】上文中 sleep(1) 的作用:

在上述代碼中,兩個(gè)線程函數(shù)中設(shè)置 sleep(1) 的主要目的是增加死鎖發(fā)生的可能性。當(dāng)線程獲取一個(gè)互斥鎖后,通過 sleep(1) 讓線程暫停一段時(shí)間,使得另一個(gè)線程有機(jī)會(huì)去獲取另一個(gè)互斥鎖,從而更有可能形成兩個(gè)線程相互等待對(duì)方持有的鎖的情況,導(dǎo)致死鎖的發(fā)生。

如果沒有這個(gè) sleep(1) ,由于線程執(zhí)行速度非常快,可能在一個(gè)線程完成對(duì)兩個(gè)鎖的獲取和操作之前,另一個(gè)線程還沒有機(jī)會(huì)執(zhí)行獲取鎖的操作,這樣死鎖就不太容易出現(xiàn),不利于演示和觀察死鎖的情況。

通過添加 sleep(1) ,模擬了線程操作中的一定延遲,使得線程之間的競(jìng)爭(zhēng)和等待更加明顯,更有可能展示出死鎖的現(xiàn)象。

編譯并執(zhí)行可執(zhí)行程序如下:

[root@localhost multi_pthread_file]# ./dead_lock
Thread 1 acquired mutex1
Thread 2 acquired mutex2

執(zhí)行可執(zhí)行程序 dead_lock ,發(fā)現(xiàn)該進(jìn)程在輸出兩條信息后卡住不動(dòng),無法繼續(xù)執(zhí)行,出現(xiàn)停滯或長(zhǎng)時(shí)間無響應(yīng)的情況,由此推斷該進(jìn)程死鎖了。

使用 gdb 命令附加到可能發(fā)生死鎖的進(jìn)程。可以通過 ps 命令找到進(jìn)程 ID(PID),然后使用 gdb 加上進(jìn)程 ID 來附加到該進(jìn)程。例如:

[root@localhost multi_pthread_file]# gdb -p 251640 -q
Attaching to process 251640
[New LWP 251641]
[New LWP 251642]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
0x00007fb09e6906cd in __pthread_timedjoin_ex () from /lib64/libpthread.so.0
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-251.el8.x86_64
(gdb) info threads
  Id   Target Id                                      Frame
* 1    Thread 0x7fb09eac8740 (LWP 251640) "dead_lock" 0x00007fb09e6906cd in __pthread_timedjoin_ex () from /lib64/libpthread.so.0
  2    Thread 0x7fb09e2b0700 (LWP 251641) "dead_lock" 0x00007fb09e69885d in __lll_lock_wait () from /lib64/libpthread.so.0
  3    Thread 0x7fb09daaf700 (LWP 251642) "dead_lock" 0x00007fb09e69885d in __lll_lock_wait () from /lib64/libpthread.so.0
(gdb)

使用 info threads 命令列出進(jìn)程中的所有線程。這將顯示每個(gè)線程的 ID 和當(dāng)前狀態(tài)。死鎖的線程通常會(huì)顯示為在等待鎖(例如,在 __lll_lock_wait)。根據(jù)上述結(jié)果可知,該進(jìn)程已死鎖。

3互斥鎖死鎖檢測(cè)和恢復(fù)

以下是一個(gè)使用 C 語言實(shí)現(xiàn)互斥鎖、死鎖檢測(cè)和恢復(fù)的簡(jiǎn)單示例代碼:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>

pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;

int lock1_held = 0;
int lock2_held = 0;

void *thread1(void *arg) {
    pthread_mutex_lock(&lock1);
    printf("Thread 1 acquired lock 1\n");
    lock1_held = 1;

    sleep(1);  // 模擬耗時(shí)操作

    while (lock2_held && lock1_held) {  // 持續(xù)檢測(cè)死鎖條件
        printf("Thread 1 detects potential deadlock and releases lock 1\n");
        pthread_mutex_unlock(&lock1);
        lock1_held = 0;
        sleep(1);  // 等待一段時(shí)間后再次嘗試
        pthread_mutex_lock(&lock1);
        lock1_held = 1;
    }

    pthread_mutex_lock(&lock2);
    printf("Thread 1 acquired lock 2\n");

    pthread_mutex_unlock(&lock2);
    pthread_mutex_unlock(&lock1);
    lock1_held = 0;
    lock2_held = 0;

    return NULL;
}

void *thread2(void *arg) {
    pthread_mutex_lock(&lock2);
    printf("Thread 2 acquired lock 2\n");
    lock2_held = 1;

    sleep(1);  // 模擬耗時(shí)操作

    while (lock1_held && lock2_held) {  // 持續(xù)檢測(cè)死鎖條件
        printf("Thread 2 detects potential deadlock and releases lock 2\n");
        pthread_mutex_unlock(&lock2);
        lock2_held = 0;
        sleep(1);  // 等待一段時(shí)間后再次嘗試
        pthread_mutex_lock(&lock2);
        lock2_held = 1;
    }

    pthread_mutex_lock(&lock1);
    printf("Thread 2 acquired lock 1\n");

    pthread_mutex_unlock(&lock1);
    pthread_mutex_unlock(&lock2);
    lock1_held = 0;
    lock2_held = 0;

    return NULL;
}

int main() {
    pthread_t t1, t2;

    pthread_create(&t1, NULL, thread1, NULL);
    pthread_create(&t2, NULL, thread2, NULL);

    sleep(5);  // 等待一段時(shí)間,讓死鎖有機(jī)會(huì)發(fā)生

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return 0;
}

在上述示例中,我們進(jìn)行了如下操作:

1)定義了兩個(gè)互斥鎖 lock1 和 lock2,以及兩個(gè)標(biāo)志 lock1_held 和 lock2_held 來跟蹤鎖的持有狀態(tài)。

2)thread1 函數(shù):

  • 首先獲取 lock1 并設(shè)置相應(yīng)的標(biāo)志。
  • 經(jīng)過一段模擬耗時(shí)操作后,進(jìn)入一個(gè) while 循環(huán),持續(xù)檢測(cè)是否同時(shí)持有 lock1 和 lock2 導(dǎo)致死鎖。
  • 如果檢測(cè)到死鎖,打印提示信息,釋放 lock1,將標(biāo)志重置,等待一段時(shí)間后重新獲取 lock1 并再次設(shè)置標(biāo)志。
  • 如果沒有死鎖,獲取 lock2,完成操作后釋放兩個(gè)鎖并重置標(biāo)志。

3)thread2 函數(shù):

  • 與 thread1 函數(shù)類似,首先獲取 lock2 并設(shè)置標(biāo)志。
  • 經(jīng)過模擬耗時(shí)操作后,在 while 循環(huán)中檢測(cè)死鎖情況并進(jìn)行相應(yīng)處理。
  • 最終獲取 lock1,完成操作后釋放鎖和重置標(biāo)志。

4)main 函數(shù):

  • 創(chuàng)建兩個(gè)線程分別執(zhí)行 thread1 和 thread2 函數(shù)。
  • 等待一段時(shí)間,讓死鎖有機(jī)會(huì)發(fā)生。
  • 等待兩個(gè)線程結(jié)束。
[root@localhost multi_pthread_file]# ./dead_lock_detect
Thread 1 acquired lock 1
Thread 2 acquired lock 2
Thread 1 detects potential deadlock and releases lock 1
Thread 2 detects potential deadlock and releases lock 2
Thread 1 acquired lock 2
Thread 2 acquired lock 1

通過這種方式,每個(gè)線程在獲取第二個(gè)鎖之前,持續(xù)檢測(cè)死鎖情況,并在可能死鎖時(shí)采取釋放已持有的鎖、等待后重新嘗試獲取的策略,以盡量避免死鎖的發(fā)生。但需要注意的是,這仍然不是一種完全可靠的死鎖避免機(jī)制,在復(fù)雜的多線程環(huán)境中,可能需要更完善的同步和協(xié)調(diào)策略。

4.固定順序加鎖避免死鎖

以下是一個(gè)使用 C 語言實(shí)現(xiàn)互斥鎖通過固定加鎖順序來避免死鎖的發(fā)生簡(jiǎn)單示例代碼:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 互斥鎖
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;

// 死鎖檢測(cè)標(biāo)志
int deadlockDetected = 0;

void *thread1Function(void *arg) {
    pthread_mutex_lock(&mutex1);
    printf("Thread 1 acquired mutex1\n");

    sleep(1);

    pthread_mutex_lock(&mutex2);
    printf("Thread 1 acquired mutex2\n");

    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);

    return NULL;
}

void *thread2Function(void *arg) {
    pthread_mutex_lock(&mutex1);
    printf("Thread 2 acquired mutex1\n");

    sleep(1);

    pthread_mutex_lock(&mutex2);
    printf("Thread 2 acquired mutex2\n");

    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);

    return NULL;
}

// 模擬死鎖檢測(cè)
void detectDeadlock() {
    sleep(3);
    // 由于固定了加鎖順序,這里不會(huì)發(fā)生死鎖,無需檢測(cè)
}

int main() {
    pthread_t thread1, thread2;

    pthread_mutex_init(&mutex1, NULL);
    pthread_mutex_init(&mutex2, NULL);

    pthread_create(&thread1, NULL, thread1Function, NULL);
    pthread_create(&thread2, NULL, thread2Function, NULL);

    detectDeadlock();

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);

    return 0;
}

在上述示例中,我們進(jìn)行了如下操作:

  1. 首先,定義了兩個(gè)互斥鎖 mutex1 和 mutex2,以及一個(gè)標(biāo)志 deadlockDetected 用于死鎖檢測(cè)(但在當(dāng)前代碼中未實(shí)際使用)。
  2. thread1Function 和 thread2Function 分別是兩個(gè)線程的執(zhí)行函數(shù)。

thread1Function 先獲取 mutex1,等待 1 秒后再獲取 mutex2,最后釋放兩個(gè)鎖。

thread2Function 邏輯相同,也是先獲取 mutex1,等待 1 秒后獲取 mutex2,最后釋放。

  1. detectDeadlock 函數(shù)用于模擬死鎖檢測(cè),但由于當(dāng)前固定了加鎖順序,實(shí)際上不會(huì)發(fā)生死鎖,所以此函數(shù)內(nèi)未進(jìn)行真正的檢測(cè)操作。
  2. 在 main 函數(shù)中:

初始化兩個(gè)互斥鎖。

創(chuàng)建兩個(gè)線程分別執(zhí)行 thread1Function 和 thread2Function 。

調(diào)用 detectDeadlock 函數(shù)進(jìn)行死鎖檢測(cè)(但如前所述,此處在當(dāng)前代碼中未實(shí)際生效)。

使用 pthread_join 等待兩個(gè)線程結(jié)束。

最后銷毀兩個(gè)互斥鎖。 總的來說,這段代碼創(chuàng)建了兩個(gè)線程并嘗試獲取兩個(gè)互斥鎖,但由于固定的加鎖順序,不會(huì)產(chǎn)生死鎖。

程序運(yùn)行結(jié)果如下:

[root@localhost multi_pthread_file]# ./dead_lock_detect_fixed_mutex
Thread 1 acquired mutex1
Thread 1 acquired mutex2
Thread 2 acquired mutex1
Thread 2 acquired mutex2

通過程序運(yùn)行結(jié)果可知,多個(gè)線程使用固定的加鎖順序,不會(huì)產(chǎn)生死鎖。

5.pthread_mutex_trylock()函數(shù)避免死鎖

以下是一個(gè)使用 C 語言實(shí)現(xiàn)互斥鎖通過 pthread_mutex_trylock() 來避免死鎖的發(fā)生簡(jiǎn)單示例代碼:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

pthread_mutex_t lock1, lock2;

void *thread1(void *arg) {
    while (1) {
        // 循環(huán)嘗試獲取鎖 1
        if (pthread_mutex_trylock(&lock1) == 0) {
            printf("Thread 1: Acquired lock 1\n");
            break;
        }
        printf("Thread 1: Failed to acquire lock 1, retrying...\n");
        sleep(1);  // 等待一段時(shí)間再重試
    }

    // 模擬一些操作
    sleep(1);

    while (1) {
        // 循環(huán)嘗試獲取鎖 2
        if (pthread_mutex_trylock(&lock2) == 0) {
            printf("Thread 1: Acquired lock 2\n");
            break;
        }
        printf("Thread 1: Failed to acquire lock 2, retrying...\n");
        pthread_mutex_unlock(&lock1);  // 釋放鎖 1
        sleep(1);  // 等待一段時(shí)間再重試
    }

    // 釋放鎖 2 和鎖 1
    pthread_mutex_unlock(&lock2);
    pthread_mutex_unlock(&lock1);

    return NULL;
}

void *thread2(void *arg) {
    while (1) {
        // 循環(huán)嘗試獲取鎖 2
        if (pthread_mutex_trylock(&lock2) == 0) {
            printf("Thread 2: Acquired lock 2\n");
            break;
        }
        printf("Thread 2: Failed to acquire lock 2, retrying...\n");
        sleep(1);  // 等待一段時(shí)間再重試
    }

    // 模擬一些操作
    sleep(1);

    while (1) {
        // 循環(huán)嘗試獲取鎖 1
        if (pthread_mutex_trylock(&lock1) == 0) {
            printf("Thread 2: Acquired lock 1\n");
            break;
        }
        printf("Thread 2: Failed to acquire lock 1, retrying...\n");
        pthread_mutex_unlock(&lock2);  // 釋放鎖 2
        sleep(1);  // 等待一段時(shí)間再重試
    }

    // 釋放鎖 1 和鎖 2
    pthread_mutex_unlock(&lock1);
    pthread_mutex_unlock(&lock2);

    return NULL;
}

int main() {
    pthread_t t1, t2;

    // 初始化鎖
    pthread_mutex_init(&lock1, NULL);
    pthread_mutex_init(&lock2, NULL);

    // 創(chuàng)建線程
    pthread_create(&t1, NULL, thread1, NULL);
    pthread_create(&t2, NULL, thread2, NULL);

    // 等待線程結(jié)束
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    // 銷毀鎖
    pthread_mutex_destroy(&lock1);
    pthread_mutex_destroy(&lock2);

    return 0;
}

在上述示例中,我們進(jìn)行了如下操作:

  1. 首先,在程序中定義了兩個(gè)互斥鎖 lock1 和 lock2,用于控制線程對(duì)資源的訪問。
  2. thread1 函數(shù):

它通過一個(gè)無休止的 while 循環(huán)來不斷嘗試獲取 lock1 鎖。

一旦成功獲取,會(huì)輸出相應(yīng)的成功獲取信息,并立即跳出當(dāng)前的循環(huán)。

倘若獲取失敗,會(huì)輸出失敗提示,并等待 1 秒鐘,然后繼續(xù)下一輪的獲取嘗試。

在成功獲取 lock1 之后,經(jīng)過 1 秒鐘的模擬操作,再次進(jìn)入另一個(gè) while 循環(huán),嘗試獲取 lock2 。

若獲取 lock2 失敗,會(huì)輸出失敗信息,同時(shí)釋放之前已獲取到的 lock1 ,接著等待 1 秒鐘,再次進(jìn)行獲取嘗試。

當(dāng)成功獲取到 lock2 后,按順序釋放 lock2 和 lock1 ,完成線程的操作。

  1. thread2 函數(shù):

其邏輯與 thread1 相似。首先通過無限的 while 循環(huán)嘗試獲取 lock2 。

成功獲取時(shí)輸出信息并跳出循環(huán),失敗則輸出提示、等待 1 秒后重新嘗試。

在成功獲取 lock2 并經(jīng)過 1 秒模擬操作后,進(jìn)入新的循環(huán)嘗試獲取 lock1 。

若獲取 lock1 失敗,同樣輸出失敗提示,釋放 lock2 ,等待 1 秒后重試。

最終成功獲取并按序釋放 lock1 和 lock2 。

  1. main 函數(shù):

聲明了兩個(gè)線程變量 t1 和 t2 。

對(duì)兩個(gè)互斥鎖進(jìn)行初始化操作。

分別創(chuàng)建兩個(gè)線程,讓它們執(zhí)行 thread1 和 thread2 函數(shù)。

使用 pthread_join 函數(shù)等待這兩個(gè)線程完成執(zhí)行。

最后,銷毀兩個(gè)互斥鎖,釋放相關(guān)資源。

這種通過循環(huán)嘗試獲取鎖的方式,能夠一定程度上應(yīng)對(duì)獲取鎖失敗的情況,避免線程因單次獲取失敗而直接終止或出現(xiàn)錯(cuò)誤,增強(qiáng)了程序的穩(wěn)定性和適應(yīng)性。但需要注意,這種頻繁的重試可能會(huì)帶來一定的系統(tǒng)開銷,所以在實(shí)際運(yùn)用中,要根據(jù)具體場(chǎng)景合理設(shè)定等待時(shí)間和重試策略,以達(dá)到性能和可靠性的平衡。

程序運(yùn)行結(jié)果如下:

[root@localhost multi_pthread_file]# ./dead_lock_avoid
Thread 1: Acquired lock 1
Thread 2: Acquired lock 2
Thread 1: Failed to acquire lock 2, retrying...
Thread 2: Failed to acquire lock 1, retrying...
Thread 1: Acquired lock 2
Thread 2: Acquired lock 1

通過程序運(yùn)行結(jié)果可知,多個(gè)線程使用 pthread_mutex_trylock 加鎖,可以避免死鎖。

七、總結(jié)

互斥鎖(Mutex,Mutual Exclusion Lock)是一種用于多線程環(huán)境中的同步機(jī)制,具有以下關(guān)鍵特點(diǎn)和用途:

  1. 資源保護(hù):確保在同一時(shí)刻只有一個(gè)線程能夠訪問被保護(hù)的共享資源,防止數(shù)據(jù)競(jìng)爭(zhēng)和不一致性。
  2. 原子操作:保證對(duì)共享資源的操作是原子性的,即要么完全執(zhí)行,要么完全不執(zhí)行,避免中間狀態(tài)被其他線程觀察到。
  3. 同步協(xié)調(diào):使多個(gè)線程能夠按照預(yù)定的順序和條件進(jìn)行協(xié)作,避免混亂和錯(cuò)誤。
  4. 互斥性:當(dāng)一個(gè)線程獲取互斥鎖后,其他線程在該鎖被釋放之前無法獲取,從而實(shí)現(xiàn)對(duì)關(guān)鍵代碼段或資源的獨(dú)占訪問。

在使用互斥鎖時(shí),需要注意正確的加鎖和解鎖順序,避免死鎖等問題。同時(shí),過度使用互斥鎖可能導(dǎo)致性能下降,因?yàn)榫€程可能會(huì)因等待鎖而阻塞。因此,在設(shè)計(jì)多線程程序時(shí),需要仔細(xì)考慮互斥鎖的使用位置和時(shí)機(jī),以達(dá)到最佳的性能和正確性平衡。

責(zé)任編輯:武曉燕 來源: Linux二進(jìn)制
相關(guān)推薦

2024-07-05 08:32:36

2024-07-08 12:51:05

2024-07-25 11:53:53

2020-09-28 06:49:50

Linux系統(tǒng)編程互斥量mutex

2010-01-21 11:27:30

linux多線程機(jī)制線程同步

2010-03-15 16:31:34

Java多線程

2020-08-26 08:59:58

Linux線程互斥鎖

2011-11-23 10:09:19

Java線程機(jī)制

2021-03-24 08:02:58

C語言

2025-02-17 02:00:00

Monitor機(jī)制代碼

2024-06-24 08:10:00

C++互斥鎖

2023-12-24 12:33:20

互斥鎖Go代碼

2025-05-06 08:20:00

互斥鎖C++編程

2023-06-02 08:29:24

https://wwMutex

2023-06-09 07:59:37

多線程編程鎖機(jī)制

2017-12-15 10:20:56

MySQLInnoDB同步機(jī)制

2019-05-27 14:40:43

Java同步機(jī)制多線程編程

2009-11-28 20:24:13

Linux互斥鎖同步移植

2025-03-31 00:01:12

2012-07-27 10:02:39

C#
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品综合久久 | 亚洲一区二区三区在线播放 | 久草新视频 | 一级片aaa | 成人高清在线 | 免费视频成人国产精品网站 | 精品久久久久久红码专区 | 日韩中文久久 | 精品色 | 欧美国产视频 | 91精品久久久久久久久 | 日本在线中文 | 91精品国模一区二区三区 | 香蕉国产在线视频 | 国产一二区在线 | 一区二区三区四区电影视频在线观看 | 欧美精品一区二区三区在线播放 | 亚洲高清在线观看 | 精品一区二区三区入口 | 搞av.com| 国产精品国产a级 | 亚洲精品www久久久 www.蜜桃av | 日本一区二区不卡视频 | 精品小视频 | 成人国内精品久久久久一区 | 久久久www成人免费精品 | 久草在线影 | 岛国精品 | av大片 | 色精品| 国产精品一区二区三 | 黄色成人av| 亚洲一区精品在线 | 欧美一区二区三区在线看 | 91精品久久久久久久99 | www,黄色,com| 久久综合一区二区三区 | 国产高清美女一级a毛片久久w | 久久伊人青青草 | 国产精品视频一区二区三区 | 久久久久av |