天天看點

Android NDK POSIX 多線程程式設計

閱讀本文大概需要 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 --