在 C 語言中使用互斥鎖

Jinku Hu 2021年2月28日
在 C 語言中使用互斥鎖

本文將介紹幾種在 C 語言中使用互斥鎖的方法。

使用 pthread_mutex_t 型別和 pthread_mutex_lock 函式來守護程式碼的關鍵部分

執行緒共享地址空間,這意味著對全域性變數等共享資料的修改必須同步,否則,將出現不正確的程式行為。請注意,下面的程式碼用 pthread_create 呼叫建立了 4 個額外的執行緒,並傳遞 func3 作為它們執行的起點。func3 在 10000 次迭代 for 迴圈中逐一修改全域性變數 shared。因此,如果四個執行緒分別將 shared 的值遞增 10000,程式應該輸出 40000。

如果執行下面的程式碼,結果將是某個隨機整數,但不是 40000。這種行為一般被歸為競賽條件,意味著給定的執行緒在訪問共享變數時沒有相互協商即同步。即,往往當它們執行迴圈交錯時,對共享變數的訪問和儲存達到不一致,最後得出錯誤的和。

多個執行緒修改記憶體中同一個物件的程式碼部分稱為關鍵部分。一般來說,關鍵部分應該用某種型別的鎖來保護,迫使其他執行緒等到當前執行緒完成執行,並確保它們都能得到正確的增量值。Mutex 是其中一種鎖型別,可以利用它來保護關鍵部分,就像 func3 中的這個 for 迴圈一樣。

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

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

int shared = 0;

void *func3(void* param)
{
    printf("Incrementing the shared variable...\n");
    for (int i = 0; i < 10000; ++i) {
        shared += 1;
    }
    return 0;
}

int main()
{
    pthread_t threads[NUM_THREADS];

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

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

    printf("%d\n", shared);
    exit(EXIT_SUCCESS);
}

輸出:

Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
30384

在這種情況下,我們將利用 POSIX 執行緒庫及其內建的 pthread_mutex_t 型別。pthread_mutex_t 型別變數通常被宣告為 static 儲存持續時間。互斥鎖只能在使用前應該只初始化一次。當互斥鎖被宣告為 static 時,應該使用 PTHREAD_MUTEX_INITIALIZER 巨集來初始化它。當互斥鎖被初始化後,執行緒就可以相應地使用 pthread_mutex_lockpthread_mutex_unlock 函式。pthread_mutex_lock 鎖定作為唯一引數傳遞的 mutex 物件。如果互斥鎖已經被鎖定,呼叫執行緒會被阻塞,直到互斥鎖變得可用。應該呼叫 pthread_mutex_unlock 來解鎖互斥鎖。如果有執行緒在同一個互斥鎖上等待,則由排程策略決定哪個執行緒獲得物件鎖。最後,我們對四個執行緒中的每一個執行緒都呼叫 pthread_join,並列印出整數-shared,在這種情況下,它應該有正確的值儲存。

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

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

int shared = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *func3(void* param)
{
    pthread_mutex_lock(&mutex);
    printf("Incrementing the shared variable...\n");
    for (int i = 0; i < 10000; ++i) {
        shared += 1;
    }
    pthread_mutex_unlock(&mutex);
    return 0;
}

int main()
{
    pthread_t threads[NUM_THREADS];

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

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

    printf("%d\n", shared);
    exit(EXIT_SUCCESS);
}

輸出:

Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
40000
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn