參考文獻:併發
在 OS 中,每一個進程都獨立地擁有:函數
所以,使用 fork
開啓一個新的進程,須要拷貝不少數據,開銷較大。線程
與進程不一樣,線程須要只須要獨立擁有:code
須要特別注意的是,文件描述符和堆空間是進程獨有的,所以該進程下面的全部線程都共用該進程的堆與文件描述符。隊列
例以下面的代碼:進程
#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
這是由於 work1
和 work2
是併發執行的,假設一開始,2 個線程同時計算 value + i
,work1
和 work2
分別獲得 1
和 5001
,可是寫入 value
變量是有前後順序的。假設 work1
先寫入,work2
後寫入,那麼對於這 2 次累加,value
的最終結果是 5001
,而不是 5002
。資源
從這個例子能夠看出,線程與進程相似,一樣須要同步 (Synchronization) ,對於臨界資源,每次只容許一個線程訪問。get
互斥量,也叫互斥鎖。mutex, 即 Mutual exclusion , 意爲相互排斥,主要用於實現線程同步中的寫保護操做。
初始化一個互斥量 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()
調用會進入到這個互斥鎖量的等待隊列中,並會進入阻塞狀態, 直到拿到該鎖以後纔會返回。
函數原型以下:
int pthread_mutex_lock(pthread_mutex_t *mutex);
非阻塞調用。當請求的鎖正在被佔用的時候, 不會進入阻塞狀態,而是馬上返回,並返回一個錯誤代碼 EBUSY,意思是說, 有其它線程正在使用這個鎖。
直白點的說法,請求資源,能拿到就拿,拿不到我就繼續往下執行。
函數原型:
int pthread_mutex_trylock(pthread_mutex_t *mutex);
釋放互斥鎖。
函數原型以下:
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); }