閱讀本文大概需要 3.25 分鐘。
本篇詳細介紹一下 POSIX 多線程程式設計常用的 API 。
1. POSIX
POSIX 全稱是 Portable Operating System Interface of UNIX ,表示可移植作業系統接口,本質上是一種程式設計标準。它定義了作業系統應該為應用程式提供的接口标準,是 IEEE 為要在各種 UNIX 作業系統上運作的軟體而定義的一系列 API 标準的總稱。
2. Pthreads
POSIX 線程是 POSIX 的線程标準,也稱為 Pthreads ,它定義了建立和管理線程的一套 API 。本文的 Pthreads 是實作 POSIX 線程标準的 c 語言程式設計庫。在 Linux 系統中,一般多線程的實作由 POSIX 多線程程式設計實作,而 Android 系統基于 Linux 系統,原生便支援 POSIX 多線程程式設計。
POSIX Linux 編譯指令:gcc hello.c -o hello -lpthread,執行指令:./hello 。
由于本文講的是 NDK 開發,代碼編譯基于 Android 平台實作。
3. POSIX 線程建立
線程建立相關 API:
1. pthread_t 線程 id 。
2. pthread_create 負責建立線程,傳入 pthread_t 的指針,線程的 執行方法和傳入線程的參數。
3. pthread_join 使目前線程挂起,等待指定線程執行結束,并擷取 線程傳回值。
4. pthread_exit 退出目前線程,并且可以設定目前線程的傳回值。
5. pthread_cancel 終止目前線程。
#include <stdlib.h>
#include <unistd.h>
#include "hello-thread.h"
#define NUM_THREADS 5
// 類似于 Java Runnable
void *run(void *arg){
// 取傳入目前線程的參數
char *thread_tag = (char*)arg;
for (int i = 0; i < 5; ++i) {
LOGD("%s thread %d", thread_tag, i);
if (i == 4) {
// 結束目前線程,參數為線程結束後的傳回值
pthread_exit(thread_tag);
//pthread_cancel(); send a cancellation request to a thread
}
}
return 0; // 線程正常執行完成後的傳回值
}
void create_threads(){
LOGD("Main thread");
char tag_arr[][5] = {"No.1","No.2","No.3","No.4","No.5"};
//線程 id ,用于區分線程,一個線程對應一個唯一的 id
pthread_t tids[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; ++i) {
// 建立線程,指定 run 方法,傳入參數 tags[i]
pthread_create(&tids[i], NULL, run, (void *) tag_arr[i]);
}
void *return_val[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; ++i) {
// 阻塞目前線程,等待指定 tid 的線程結束,并擷取線程傳回值
// join with a terminated thread
pthread_join(tids[i], &return_val[i]);
LOGD("thread %s terminated.", (char*)return_val[i]);
}
}
複制
運作結果:
I/hello-thread: Main thread
I/hello-thread: No.1 thread 0
I/hello-thread: No.1 thread 1
I/hello-thread: No.1 thread 2
I/hello-thread: No.1 thread 3
I/hello-thread: No.1 thread 4
I/hello-thread: No.2 thread 0
I/hello-thread: No.2 thread 1
I/hello-thread: No.2 thread 2
I/hello-thread: No.2 thread 3
I/hello-thread: No.2 thread 4
I/hello-thread: No.3 thread 0
I/hello-thread: No.3 thread 1
I/hello-thread: No.3 thread 2
I/hello-thread: No.3 thread 3
I/hello-thread: No.3 thread 4
I/hello-thread: No.4 thread 0
I/hello-thread: No.4 thread 1
I/hello-thread: No.4 thread 2
I/hello-thread: No.4 thread 3
I/hello-thread: No.4 thread 4
I/hello-thread: thread No.1 terminated.
I/hello-thread: No.5 thread 0
I/hello-thread: No.5 thread 1
I/hello-thread: No.5 thread 2
I/hello-thread: No.5 thread 3
I/hello-thread: No.5 thread 4
I/hello-thread: thread No.2 terminated.
I/hello-thread: thread No.3 terminated.
I/hello-thread: thread No.4 terminated.
I/hello-thread: thread No.5 terminated.
複制
3. POSIX 線程同步
線程同步相關 API :
1. pthread_mutex_t 互斥鎖。
2. pthread_mutex_init 初始化互斥鎖,需傳入互斥鎖的指針。
3. pthread_mutex_destroy 銷毀互斥鎖,需傳入互斥鎖的指針。
4. pthread_mutex_lock 加鎖,需傳入互斥鎖的指針。
5. pthread_mutex_unlock 解鎖,需傳入互斥鎖的指針
#include <stdlib.h>
#include <unistd.h>
#include "hello-thread.h"
int g_count = 0;
// 互斥鎖
pthread_mutex_t mutex;
void *asyn_run(void *arg){
// lock
pthread_mutex_lock(&mutex);
// 取傳入目前線程的參數
char *thread_tag = (char*)arg;
for (int i = 0; i < 10; ++i) {
// 休眠 200 ms
usleep(200 * 1000);
g_count ++;
LOGD("%s thread %d, g_count = %d", thread_tag, i, g_count);
}
// unlock
pthread_mutex_unlock(&mutex);
return thread_tag; // 線程正常執行完成後的傳回值
}
void syn_thread(){
LOGD("Main thread");
// 初始化互斥鎖
pthread_mutex_init(&mutex, NULL);
pthread_t t1, t2;
// 建立 2 個線程
pthread_create(&t1, NULL, asyn_run, "No.1");
pthread_create(&t2, NULL, asyn_run, "No.2");
void *rtn_val[2];
pthread_join(t1, &rtn_val[0]);
pthread_join(t2, &rtn_val[1]);
LOGD("thread %s terminated.", (char*)rtn_val[0]);
LOGD("thread %s terminated.", (char*)rtn_val[1]);
// 銷毀互斥鎖
pthread_mutex_destroy(&mutex);
}
複制
運作結果:
I/hello-thread: No.1 thread 0, g_count = 1
I/hello-thread: No.1 thread 1, g_count = 2
I/hello-thread: No.1 thread 2, g_count = 3
I/hello-thread: No.1 thread 3, g_count = 4
I/hello-thread: No.1 thread 4, g_count = 5
I/hello-thread: No.1 thread 5, g_count = 6
I/hello-thread: No.1 thread 6, g_count = 7
I/hello-thread: No.1 thread 7, g_count = 8
I/hello-thread: No.1 thread 8, g_count = 9
I/hello-thread: No.1 thread 9, g_count = 10
I/hello-thread: No.2 thread 0, g_count = 11
I/hello-thread: No.2 thread 1, g_count = 12
I/hello-thread: No.2 thread 2, g_count = 13
I/hello-thread: No.2 thread 3, g_count = 14
I/hello-thread: No.2 thread 4, g_count = 15
I/hello-thread: No.2 thread 5, g_count = 16
I/hello-thread: No.2 thread 6, g_count = 17
I/hello-thread: No.2 thread 7, g_count = 18
I/hello-thread: No.2 thread 8, g_count = 19
I/hello-thread: No.2 thread 9, g_count = 20
I/hello-thread: thread No.1 terminated.
I/hello-thread: thread No.2 terminated.
複制
5. POSIX 線程間通信
線程間通信相關 API :
1. pthread_cond_t 條件變量,條件變量是線程同步的一種手段,使 線程可以休眠等待某種條件出現。
2. pthread_cond_signal 發送一個信号給另外一個正在處于阻塞等 待狀态的線程,原本這兩個線程競争同一個 mutex lock 。
3. pthread_cond_wait 使目前線程處于阻塞狀态,直到接收到其他 線程發送對應的 condsignal 。
4. pthread_cond_init 初始化條件變量。
5. pthread_cond_destroy 銷毀條件變量。
#include <stdlib.h>
#include <unistd.h>
#include "hello-thread.h"
// 共享資料
volatile int shared_count = 0;
pthread_mutex_t pthread_mutex;
// 條件變量
pthread_cond_t pthread_cond;
void *producer(void *arg){
char *tag = (char*)arg;
for (;;) {
pthread_mutex_lock(&pthread_mutex);
// 生産者生産産品
shared_count ++;
LOGD("%s thread 生産産品, count = %d", tag, shared_count);
// 通知消費者線程消費
pthread_cond_signal(&pthread_cond);
pthread_mutex_unlock(&pthread_mutex);
// 休眠 200 ms
usleep(200 * 1000);
}
return (void*)0;
}
void *consumer(void *arg){
char* tag = (char*)arg;
for(;;){
pthread_mutex_lock(&pthread_mutex);
while (shared_count == 0){
// 當沒有産品可以消費時,等待生産者生産(等待條件變量被喚醒,目前線程釋放互斥鎖)
// 當被其他線程喚醒時,解除阻塞狀态,重新申請獲得互斥鎖
pthread_cond_wait(&pthread_cond, &pthread_mutex);
}
shared_count --;
LOGD("%s thread 消費産品, count = %d", tag, shared_count);
pthread_mutex_unlock(&pthread_mutex);
// 休眠 500 ms
usleep(500 * 1000);
}
return (void*)0;
};
// 線程間通信
void communicate_thread(){
pthread_mutex_init(&pthread_mutex, NULL);
// 初始化條件變量
pthread_cond_init(&pthread_cond, NULL);
// 線程 id
pthread_t producer_tid, consumer_tid;
// 建立生産者線程
pthread_create(&producer_tid, NULL, producer, "producer");
// 建立消費者線程
pthread_create(&consumer_tid, NULL, consumer, "consumer");
// 等待線程結束
pthread_join(producer_tid, NULL);
pthread_join(consumer_tid, NULL);
// 銷毀互斥鎖
pthread_mutex_destroy(&pthread_mutex);
// 銷毀條件變量
pthread_cond_destroy(&pthread_cond);
}
複制
運作結果:
I/hello-thread: producer thread 生産産品, count = 1
I/hello-thread: consumer thread 消費産品, count = 0
I/hello-thread: producer thread 生産産品, count = 1
I/hello-thread: producer thread 生産産品, count = 2
I/hello-thread: consumer thread 消費産品, count = 1
I/hello-thread: producer thread 生産産品, count = 2
I/hello-thread: producer thread 生産産品, count = 3
I/hello-thread: consumer thread 消費産品, count = 2
I/hello-thread: producer thread 生産産品, count = 3
I/hello-thread: producer thread 生産産品, count = 4
I/hello-thread: producer thread 生産産品, count = 5
I/hello-thread: consumer thread 消費産品, count = 4
I/hello-thread: producer thread 生産産品, count = 5
I/hello-thread: producer thread 生産産品, count = 6
I/hello-thread: consumer thread 消費産品, count = 5
......
複制
-- END --