pthread 互斥量

參考文獻:併發

溫故知新

在 OS 中,每一個進程都獨立地擁有:函數

  • Process ID, process group ID, user ID, and group ID
  • Environment
  • Working directory
  • Program instructions
  • Registers
  • Stack
  • Heap
  • File descriptors
  • Signal actions
  • Shared libraries
  • Inter-process communication tools (such as message queues, pipes, semaphores, or shared memory).

所以,使用 fork 開啓一個新的進程,須要拷貝不少數據,開銷較大。線程

與進程不一樣,線程須要只須要獨立擁有:code

  • Stack pointer
  • Registers
  • Scheduling properties (such as policy or priority)
  • Set of pending and blocked signals
  • Thread specific data

須要特別注意的是,文件描述符和堆空間是進程獨有的,所以該進程下面的全部線程都共用該進程的堆與文件描述符。隊列

例以下面的代碼:進程

#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
void *worker1(void *arg)
{
    char *p = malloc(25);
    memcpy(p, "heap data from worker1", 23);
    return p;
};
void *worker2(void *arg)
{
    pthread_t tid1 = *(pthread_t *)arg;
    char *ptr = NULL;
    pthread_join(tid1, (void **)&ptr);
    printf("In worker2: ");
    if (ptr) puts(ptr);
    return NULL;
}
int main()
{
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, worker1, NULL);
    pthread_create(&tid2, NULL, worker2, &tid1);
    pthread_join(tid2, NULL);
}

線程同步

在描述這個概念以前,先看一段代碼:ip

#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
const int N = 1e4;
int value = 0;
void *worker1(void *arg)
{
    int i = 1;
    for (; i <= N / 2; i++) value = value + i;
    return NULL;
};
void *worker2(void *arg)
{
    int i = N / 2 + 1;
    for (; i <= N; i++) value = value + i;
    return NULL;
}
int main()
{
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, worker1, NULL);
    pthread_create(&tid2, NULL, worker2, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    printf("%d\n", value);
    printf("SUM(1, %d) should be %d .\n", N, N * (N + 1) / 2);
}

顯然,咱們想經過 2 個線程實現 SUM(1, N) 這個功能,可是編譯屢次你會發現,value 的值並不許確,有時候能輸出正確答案 500500,有時候卻不能。ci

這是由於 work1work2 是併發執行的,假設一開始,2 個線程同時計算 value + iwork1work2 分別獲得 15001,可是寫入 value 變量是有前後順序的。假設 work1 先寫入,work2 後寫入,那麼對於這 2 次累加,value 的最終結果是 5001 ,而不是 5002資源

從這個例子能夠看出,線程與進程相似,一樣須要同步 (Synchronization) ,對於臨界資源,每次只容許一個線程訪問。get

互斥量 mutex

互斥量,也叫互斥鎖。mutex, 即 Mutual exclusion , 意爲相互排斥,主要用於實現線程同步中的寫保護操做。

pthread_mutex_init

初始化一個互斥量 pthread_mutex_t mutex .

函數原型:

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

參數:

  • mutex 是即將要被初始化的互斥量
  • attr 是互斥量的屬性,與 pthread_attr_t 相似

與之相似的,還有 pthread_mutex_destroy 函數。

使用方法:

pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_destroy(&mutex);

pthread_mutex_lock

阻塞調用。若是這個互斥鎖此時正在被其它線程佔用, 那麼 pthread_mutex_lock() 調用會進入到這個互斥鎖量的等待隊列中,並會進入阻塞狀態, 直到拿到該鎖以後纔會返回。

函數原型以下:

int pthread_mutex_lock(pthread_mutex_t *mutex);

pthread_mutex_trylock

非阻塞調用。當請求的鎖正在被佔用的時候, 不會進入阻塞狀態,而是馬上返回,並返回一個錯誤代碼 EBUSY,意思是說, 有其它線程正在使用這個鎖。

直白點的說法,請求資源,能拿到就拿,拿不到我就繼續往下執行。

函數原型:

int pthread_mutex_trylock(pthread_mutex_t *mutex);

pthread_mutex_unlock

釋放互斥鎖。

函數原型以下:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

對於這些 API,若是成功,那麼返回 0,不然返回錯誤碼 errno ,能夠經過下列宏定義打印錯誤信息:

#define handler_error_en(en, msg) \
    do                            \
    {                             \
        errno = en;               \
        perror(msg);              \
        exit(EXIT_FAILURE);       \
    } while (0)

例子:同步累加

對於「線程同步」一節給出的例子,使用互斥量實現同步操做,使得程序可以正確完成累加操做。

#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
const int N = 1e4;
int value = 0;
pthread_mutex_t mutex;
void *worker1(void *arg)
{
    int i = 1;
    int sum = 0;
    for (; i <= N / 2; i++) sum += i;
    // 這樣保證 value 僅能由一個線程訪問
    pthread_mutex_lock(&mutex);
    value += sum;
    pthread_mutex_unlock(&mutex);
    return NULL;
};
void *worker2(void *arg)
{
    int i = N / 2 + 1;
    int sum = 0;
    for (; i <= N; i++) sum += i;
    pthread_mutex_lock(&mutex);
    value += sum;
    pthread_mutex_unlock(&mutex);
    return NULL;
}
int main()
{
    pthread_t tid1, tid2;
    pthread_mutex_init(&mutex, NULL);
    pthread_create(&tid1, NULL, worker1, NULL);
    pthread_create(&tid2, NULL, worker2, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    printf("%d\n", value);
    printf("SUM(1, %d) should be %d .\n", N, N * (N + 1) / 2);
    pthread_mutex_destroy(&mutex);
}
相關文章
相關標籤/搜索