天天看點

Linux多線程程式設計基礎知識彙總!

作者:嵌入式大雜燴

大家好,我是雜燴君。

Hello系列 , 彙總短而實用的内容。

什麼是多線程程式設計?

1、線程和程序的差別

程序是指正在運作的程式,它擁有獨立的記憶體空間和系統資源,不同程序之間的資料不共享。

線程是程序内的執行單元,它與同一程序内的其他線程共享程序的記憶體空間和系統資源。

2、多線程的優勢和應用場景

多線程是一種并發程式設計方式,它的優勢包括:

  • 提高程式的響應速度和運作效率(多核CPU下的多線程)
  • 充分利用CPU資源,提高系統的使用率
  • 支援多個任務并行執行,提高程式的可擴充性和可維護性

Linux下的多線程程式設計

Linux下C語言多線程程式設計依賴于pthread多線程庫。pthread庫是Linux的多線程庫,是POSIX标準線程API的實作,它提供了一種建立和操縱線程的方法,以及一些同步機制,如互斥鎖、條件變量等。

頭檔案:

#include <pthread.h>           

編譯連結需要連結連結庫 pthread。

一、線程的基本操作

1、pthread_create

/**
 * @brief 建立一個線程
 *
 * Detailed function description
 *
 * @param[in] thread: 一個指向線程辨別符的指針,線程調用後,該值被設定為線程ID;pthread_t為unsigned long int
 * @param[in] attr: 用來設定線程屬性
 * @param[in] start_routine: 線程函數體,線程建立成功後,thread 指向的記憶體單元從該位址開始運作
 * @param[in] arg: 傳遞給線程函數體的參數
 *
 * @return 線程建立成功,則傳回0,失敗則傳回錯誤碼,并且 thread 内容是未定義的
 */
int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void*(*start_routine)(void*), void* arg);           

例子test.c:建立一個線程,每1s列印一次。

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

static pthread_t s_thread_id;
static unsigned char s_thread_running = 0;

void *thread_fun(void *arg)
{
    s_thread_running = 1;
    while (s_thread_running)
    {
        printf("thread run...\n");
        sleep(1);
    }

    pthread_exit(NULL);
}

int main(void)
{
    int ret = 0;

    printf("Before Thread\n");
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);
    if (ret != 0)
    {
        printf("thread_create error!\n");
        exit(EXIT_FAILURE);
    }

    ret = pthread_join(s_thread_id, NULL); ///< 阻塞等待線程結束
    if (ret != 0)
    {
        printf("pthread_join error!\n");
        exit(EXIT_FAILURE);
    }
    printf("After Thread\n");
    exit(EXIT_SUCCESS);
}           

編譯、運作:

gcc test.c -o test -lpthread           
Linux多線程程式設計基礎知識彙總!

2、pthread_join

/**
 * @brief 等待某個線程結束
 *
 * Detailed function description: 這是一個線程阻塞函數,調用該函數則等到線程結束才繼續運作
 *
 * @param[in] thread: 某個線程的ID
 * @param[in] retval: 用于擷取線程 start_routine 的傳回值
 *
 * @return 線程建立成功,則傳回0,失敗則傳回錯誤碼,并且 thread 内容是未定義的
 */
int pthread_join(pthread_t thread, void **retval);           

例子test.c:建立一個線程,進行一次加法運算就傳回。

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

static pthread_t s_thread_id;
static unsigned char s_thread_running = 0;

void *thread_fun(void *arg)
{
    static int res = 0;
    int a = 1, b = 2;

    res = a + b;
    sleep(1);
    printf("thread run, a + b = %d, addr = %p\n", res, &res);

    pthread_exit(&res);
}

int main(void)
{
    int ret = 0;
    int *retval = NULL;

    printf("Before Thread\n");
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);
    if (ret != 0)
    {
        printf("pthread_create error!\n");
        exit(EXIT_FAILURE);
    }

    ret = pthread_join(s_thread_id, (void **)&retval); ///< 阻塞等待線程結束
    if (ret != 0)
    {
        printf("pthread_join error!\n");
        exit(EXIT_FAILURE);
    }
    if (retval != NULL)
    {
        printf("After Thread, retval = %d, addr = %p\n", (int)*retval, retval);
    }

    exit(EXIT_SUCCESS);
}           

編譯、運作:

Linux多線程程式設計基礎知識彙總!

3、pthread_exit

/**
 * @brief 退出線程
 *
 * Detailed function description
 *
 * @param[in] retval: 它指向的資料将作為線程退出時的傳回值
 *
 * @return void
 */
void pthread_exit(void *retval);           

線程将指定函數體中的代碼執行完後自行結束;

線程執行過程中,被同一程序中的其它線程(包括主線程)強制終止;

線程執行過程中,遇到 pthread_exit() 函數結束執行。

例子test.c:建立一個線程,每個1s列印一次,列印超過5次時調用pthread_exit退出。

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

static pthread_t s_thread_id;
static unsigned char s_thread_running = 0;
const static char *thread_exit_str = "thread_exit ok!";

void *thread_fun(void *arg)
{
    static int cnt = 0;
    
    s_thread_running = 1;
    while (s_thread_running)
    {
        cnt++;
        if (cnt > 5)
        {
            pthread_exit((void*)thread_exit_str);
        }
        printf("thread run...\n");
        sleep(1);
    }

    pthread_exit(NULL);
}

int main(void)
{
    int ret = 0;
    void *thread_res = NULL;

    printf("Before Thread\n");
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);
    if (ret != 0)
    {
        printf("thread_create error!\n");
        exit(EXIT_FAILURE);
    }

    ret = pthread_join(s_thread_id, (void**)&thread_res);
    if (ret != 0)
    {
        printf("thread_join error!\n");
        exit(EXIT_FAILURE);
    }
    printf("After Thread, thread_res = %s\n", (char*)thread_res);
    exit(EXIT_SUCCESS);
}           

編譯、運作:

Linux多線程程式設計基礎知識彙總!

使用return退出線程與使用pthread_exit退出線程的差別?

return為通用的函數退出操作,pthread_exit專用與線程,既然pthread庫有提供專門的函數,自然用pthread_exit會好些,雖然使用return也可以。

看看return退出線程與使用pthread_exit退出線程的具體差別:退出主線程。使用pthread_exit退出主線程隻會終止目前線程,不會影響程序中其它線程的執行;使用return退出主線程,主線程退出執行很快,所有線程都會退出。

例子:使用pthread_exit退出主線程

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

static pthread_t s_thread_id;
static unsigned char s_thread_running = 0;
const static char *thread_exit_str = "thread_exit ok!";

void *thread_fun(void *arg)
{
    sleep(1);
    printf("thread_fun run...\n");
    pthread_exit(NULL);
}

int main(void)
{
    int ret = 0;
    void *thread_res = NULL;

    printf("Before Thread\n");
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);
    if (ret != 0)
    {
        printf("thread_create error!\n");
        exit(EXIT_FAILURE);
    }
    
    printf("main thread exit\n");
    pthread_exit(NULL);
}           

編譯、運作:

Linux多線程程式設計基礎知識彙總!

例子:使用return退出主線程

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

static pthread_t s_thread_id;
static unsigned char s_thread_running = 0;
const static char *thread_exit_str = "thread_exit ok!";

void *thread_fun(void *arg)
{
    sleep(1);
    printf("thread_fun run...\n");
    pthread_exit(NULL);
}

int main(void)
{
    int ret = 0;
    void *thread_res = NULL;

    printf("Before Thread\n");
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);
    if (ret != 0)
    {
        printf("thread_create error!\n");
        exit(EXIT_FAILURE);
    }
    
    printf("main thread exit\n");
    
    return 0;
}           

編譯、運作:

Linux多線程程式設計基礎知識彙總!

4、pthread_self

/**
 * @brief 用來擷取目前線程ID
 *
 * Detailed function description
 *
 * @param[in] void
 *
 * @return 傳回線程id
 */
pthread_t pthread_self(void);           

例子:

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

static pthread_t s_thread_id;
static unsigned char s_thread_running = 0;
const static char *thread_exit_str = "thread_exit ok!";

void *thread_fun(void *arg)
{
    static int cnt = 0;
    
    s_thread_running = 1;
    while (s_thread_running)
    {
        cnt++;
        if (cnt > 5)
        {
            pthread_exit((void*)thread_exit_str);
        }
        printf("thread run(tid = %ld)...\n", pthread_self());
        sleep(1);
    }

    pthread_exit(NULL);
}

int main(void)
{
    int ret = 0;
    void *thread_res = NULL;

    pid_t pid = getpid();
    printf("pid = %d\n", pid);

    printf("Before Thread\n");
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);
    if (ret != 0)
    {
        printf("thread_create error!\n");
        exit(EXIT_FAILURE);
    }

    ret = pthread_join(s_thread_id, (void**)&thread_res);
    if (ret != 0)
    {
        printf("thread_join error!\n");
        exit(EXIT_FAILURE);
    }
    printf("After Thread, thread_res = %s\n", (char*)thread_res);
    exit(EXIT_SUCCESS);
}           

編譯、運作:

Linux多線程程式設計基礎知識彙總!

5、pthraad_detach

/**
 * @brief 分離線程
 *
 * Detailed function description: 分離線程,線程結束是系統自動回收線程的資源
 *
 * @param[in] thread: 某個線程的ID
 *
 * @return 成功時傳回0,失敗傳回其他值
 */
int pthread_detach(pthread_t thread);           

pthread_create建立的線程有兩種狀态:joinable(可結合的)和unjoinable(不可結合的/分離的)。預設是joinable 狀态。

一個可結合的線程能夠被其他線程收回其資源和殺死;在被其他線程回收之前,它的存儲器資源(如棧)是不釋放的,是以以預設的屬性建立線程時,建立的線程時可結合的,我們需要對線程退出時調用pthread_join對線程資源進行回收。隻有當pthread_join函數傳回時,建立的線程才算終止,才能釋放自己占用的系統資源。

一個不可結合的線程,線程結束後會自動釋放占用資源。

因為pthread_join是一個阻塞的操作,而大多數時候主線程并不希望因為調用pthread_join而阻塞,并且大多數情況下不會使用線程函數體的傳回值,是以這時候可以把線程建立為不可結合的/分離的。

把線程建立為不可結合的/分離的有兩種方式:

  • 在建立線程之後,使用pthraad_detach分離線程。
  • 在建立線程之前,使用pthread_attr_setdetachstate設定線程以不可結合的/分離的狀态建立。

例子:在建立線程之後,使用pthraad_detach分離線程。

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

static pthread_t s_thread_id;
static unsigned char s_thread_running = 0;

void *thread_fun(void *arg)
{
    s_thread_running = 1;
    while (s_thread_running)
    {
        printf("child thread run...\n");
        sleep(1);
    }

    pthread_exit(NULL);
}

int main(void)
{
    int ret = 0;

    printf("Before Thread\n");
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);
    if (ret != 0)
    {
        printf("thread_create error!\n");
        exit(EXIT_FAILURE);
    }

    ret = pthread_detach(s_thread_id);
    if (ret != 0)
    {
        printf("pthread_detach error!\n");
        exit(EXIT_FAILURE);
    }
    printf("After Thread\n");

    while (1)
    {
        printf("main thread run...\n");
        sleep(1);
    }
    
    exit(EXIT_SUCCESS);
}           

編譯、運作:

Linux多線程程式設計基礎知識彙總!

pthread_join與pthraad_detach的差別:

  • pthread_detach()即主線程與子線程分離,兩者互相不幹涉,子線程結束同時子線程的資源自動回收。
  • pthread_join()即是子線程合入主線程,主線程會一直阻塞,直到子線程執行結束,然後回收子線程資源,并繼續執行。

6、pthread_attr_init

/**
 * @brief 初始化一個線程對象的屬性
 *
 * Detailed function description
 *
 * @param[in] attr: 指向一個線程屬性的指針
 *
 * @return 成功時傳回0,失敗傳回其他值
 */
int pthread_attr_init(pthread_attr_t *attr);           

如果不設定線程屬性,線程則以預設屬性進行建立,預設的屬性值如:

Linux多線程程式設計基礎知識彙總!

例子:在建立線程之前,使用pthread_attr_setdetachstate設定線程以不可結合的/分離的狀态建立。

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

static pthread_t s_thread_id;
static unsigned char s_thread_running = 0;

void *thread_fun(void *arg)
{
    s_thread_running = 1;
    while (s_thread_running)
    {
        printf("thread run...\n");
        sleep(1);
    }

    pthread_exit(NULL);
}

int main(void)
{
    int ret = 0;

    printf("Before Thread\n");
    pthread_attr_t attr;
    ret = pthread_attr_init(&attr);
    if (ret != 0)
    {
        printf("pthread_attr_init error!\n");
        exit(EXIT_FAILURE);
    }
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);  ///< 線程以分離的狀态建立
    ret = pthread_create(&s_thread_id, &attr, thread_fun, NULL);
    if (ret != 0)
    {
        printf("thread_create error!\n");
        exit(EXIT_FAILURE);
    }
    printf("After Thread\n");
    pthread_attr_destroy(&attr);  ///< 銷毀線程屬性結構

    while (1)
    {
        sleep(1);
    }
    
    exit(EXIT_SUCCESS);
}           

二、互斥鎖(mutex)的使用

互斥鎖用于保護一些公共資源。一些公共資源有可能會被多個線程共同使用,如果不做資源保護,可能會産生 意想不到的bug。

一個線程,如果需要通路公共資源,需要獲得互斥鎖并對其加鎖,資源在在鎖定過程中,如果其它線程對其進行通路,也需要獲得互斥鎖,如果擷取不到,線程隻能進行阻塞,直到獲得該鎖的線程解鎖。

互斥鎖API:

#include<pthread.h>  
///< 建立互斥對象,用指定的初始化屬性初始化互斥對象  
int pthread_mutex_init(pthread_mutex_t *mutex,  
                                       const pthread_mutex_attr_t *mutexattr);  

///< 加鎖  
int pthread_mutex_lock(pthread_mutex_t *mutex);  

///< 解鎖  
int pthread_mutex_unlock(pthread_mutex_t *mutex);  

///< 加鎖,但是如果對象已經上鎖則傳回EBUSY錯誤代碼而不阻塞  
int pthread_mmutex_trylock(pthread_mutex_t *mutex);  

///< 析構并釋放mutex相關資源  
int pthread_mutex_destroy(pthread_mutex_t *mutex);             

互斥鎖有兩種建立方式:

  • 靜态建立:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;           
  • 動态建立:
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);           

pthread互斥鎖屬性包括:

  • PTHREAD_MUTEX_TIMED_NP:這是預設值,也就是普通鎖。當一個線程加鎖以後,其餘請求鎖的線程将會形成一個等待隊列,并在解鎖後按優先級獲得鎖。這種政策可以確定資源配置設定的公平性。
  • PTHREAD_MUTEX_RECURSIVE_NP:嵌套鎖。允許同一個線程對同一個鎖成功獲得多次,并通過unlock解鎖。如果是不同線程請求,則在加鎖線程解鎖時重新競争。
  • PTHREAD_MUTEX_ERRORCHECK_NP:檢錯鎖。如果同一個線程請求同一個鎖,則傳回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP類型動作相同,這樣就保證了當不允許多次加鎖時不會出現最簡單情況下的死鎖。
  • PTHREAD_MUTEX_ADAPTIVE_NP:适應鎖,動作最簡單的鎖類型,僅等待一小段時間,如果不能獲得鎖就放棄等待

互斥鎖使用形式:

pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL);  ///< 初始化互斥鎖
pthread_mutex_lock(&mutex);       ///< 加鎖
///< 操作公共資源
pthread_mutex_unlock(&mutex);     ///< 解鎖
pthread_mutex_destroy(&mutex);    ///< 銷毀互斥鎖           

例子:

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

static pthread_t s_thread1_id;
static pthread_t s_thread2_id;
static unsigned char s_thread1_running = 0;
static unsigned char s_thread2_running = 0;

static pthread_mutex_t s_mutex;
static int s_cnt = 0;

void *thread1_fun(void *arg)
{
     printf("[%s]pthread_mutex_lock ------ s_cnt = %d\n", __FUNCTION__, s_cnt);
    pthread_mutex_lock(&s_mutex);    ///< 加鎖
    for (size_t i = 0; i < 100; i++)
    {
        s_cnt++;
    }
    printf("[%s]pthread_mutex_unlock ------ s_cnt = %d\n", __FUNCTION__, s_cnt);
    pthread_mutex_unlock(&s_mutex);  ///< 解鎖
    
    pthread_exit(NULL);
}

void *thread2_fun(void *arg)
{
    printf("[%s]pthread_mutex_lock ------ s_cnt = %d\n", __FUNCTION__, s_cnt);
    pthread_mutex_lock(&s_mutex);    ///< 加鎖
    for (size_t i = 0; i < 100; i++)
    {
        s_cnt++;
    }
    printf("[%s]pthread_mutex_unlock ------ s_cnt = %d\n", __FUNCTION__, s_cnt);
    pthread_mutex_unlock(&s_mutex);  ///< 解鎖
    
    pthread_exit(NULL);
}

int main(void)
{
    int ret = 0;

    ///< 建立互斥量
    ret = pthread_mutex_init(&s_mutex, NULL);
    if (ret != 0)
    {
        printf("pthread_mutex_init error!\n");
        exit(EXIT_FAILURE);
    }

    ///< 建立線程1
    ret = pthread_create(&s_thread1_id, NULL, thread1_fun, NULL);
    if (ret != 0)
    {
        printf("thread1_create error!\n");
        exit(EXIT_FAILURE);
    }
    ret = pthread_join(s_thread1_id, NULL); ///< 阻塞等待線程結束
    if (ret != 0)
    {
        printf("pthread1_join error!\n");
        exit(EXIT_FAILURE);
    }

    ///< 建立線程2
    ret = pthread_create(&s_thread2_id, NULL, thread2_fun, NULL);
    if (ret != 0)
    {
        printf("thread2_create error!\n");
        exit(EXIT_FAILURE);
    }
    ret = pthread_join(s_thread2_id, NULL); ///< 阻塞等待線程結束
    if (ret != 0)
    {
        printf("pthread2_join error!\n");
        exit(EXIT_FAILURE);
    }

    printf("main thread, s_cnt = %d\n", s_cnt);

    ret = pthread_mutex_destroy(&s_mutex);
    {
        printf("pthread_mutex_destroy error!\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}           

編譯、運作:

Linux多線程程式設計基礎知識彙總!

三、條件變量的使用

條件變量是線上程中以睡眠的方式等待某一條件的發生,是利用線程間共享的全局變量進行同步的一種機制。

條件變量是線程可用的一種同步機制,條件變量給多個線程提供了一個會合的場所,條件變量與互斥量一起使用時,允許線程以無競争的方式等待特定的條件發生。

條件變量API:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

///< 條件變量初始化
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

///< 銷毀條件變量
int pthread_cond_destroy(pthread_cond_t *cond);

///< 等待條件變量
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

///< 帶有逾時功能的 等待條件變量
int pthread_cond_timedwait(pthread_cond_t *restrict cond, 
						pthread_mutex_t *restrict mutex,
						const struct timespec *restrict tsptr);

///< 通知條件變量,喚醒至少1個等待該條件的線程
int pthread_cond_signal(pthread_cond_t *cond);

///< 通知條件變量,廣播喚醒等待該條件的所有線程
int pthread_cond_broadcast(pthread_cond_t *cond);           

假如有兩個線程,線程1依賴于某個變量才能執行相應的操作,而這個變量正好是由線程2來改變的。這種情況下有兩種方案編寫程式:

方案一:線程1輪詢的方式檢測這個變量是否變化,變化則執行相應的操作。

方案二:使用條件變量的方式。線程1等待線程2滿足條件時進行喚醒。

其中,方案一比較浪費CPU資源。

條件變量的例子:建立兩個線程,線程1對全局計數變量cnt從0開始進行自增操作。線程2列印5的倍數,線程1列印其它數。

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

static pthread_t s_thread1_id;
static pthread_t s_thread2_id;
static unsigned char s_thread1_running = 0;
static unsigned char s_thread2_running = 0;

static pthread_mutex_t s_mutex;
static pthread_cond_t s_cond;
static int s_cnt = 0;

void *thread1_fun(void *arg)
{
    s_thread1_running = 1;
    while (s_thread1_running)  
    {
        pthread_mutex_lock(&s_mutex);    ///< 加鎖
        s_cnt++;
        pthread_mutex_unlock(&s_mutex);  ///< 解鎖

        if (s_cnt % 5 == 0)
        {
            pthread_cond_signal(&s_cond);  ///< 喚醒其它等待該條件的線程
        }
        else
        {
            printf("[%s]s_cnt = %d\n", __FUNCTION__, s_cnt);
        }

        usleep(100 * 1000);
    }
    
    pthread_exit(NULL);
}

void *thread2_fun(void *arg)
{
    s_thread2_running = 1;
    while (s_thread2_running)
    {
        pthread_mutex_lock(&s_mutex);    ///< 加鎖
        while (s_cnt % 5 != 0)
        {
            pthread_cond_wait(&s_cond, &s_mutex);   ///< 等待條件變量
        }
        
        printf("[%s]s_cnt = %d\n", __FUNCTION__, s_cnt);
        pthread_mutex_unlock(&s_mutex);  ///< 解鎖
        
        usleep(200 * 1000);
    }
    
    pthread_exit(NULL);
}

int main(void)
{
    int ret = 0;

    ///< 建立互斥量
    ret = pthread_mutex_init(&s_mutex, NULL);
    if (ret != 0)
    {
        printf("pthread_mutex_init error!\n");
        exit(EXIT_FAILURE);
    }

    ///< 建立條件變量
    ret = pthread_cond_init(&s_cond, NULL);
    if (ret != 0)
    {
        printf("pthread_cond_init error!\n");
        exit(EXIT_FAILURE);
    }

    ///< 建立線程1
    ret = pthread_create(&s_thread1_id, NULL, thread1_fun, NULL);
    if (ret != 0)
    {
        printf("thread1_create error!\n");
        exit(EXIT_FAILURE);
    }
    ret = pthread_detach(s_thread1_id);
    if (ret != 0)
    {
        printf("s_thread1_id error!\n");
        exit(EXIT_FAILURE);
    }

    ///< 建立線程2
    ret = pthread_create(&s_thread2_id, NULL, thread2_fun, NULL);
    if (ret != 0)
    {
        printf("thread2_create error!\n");
        exit(EXIT_FAILURE);
    }
    ret = pthread_detach(s_thread2_id);
    if (ret != 0)
    {
        printf("s_thread2_id error!\n");
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        sleep(1);
    }

    return 0;
}           

編譯、運作:

Linux多線程程式設計基礎知識彙總!

以上就是本次的分享,如果覺得文章有用,歡迎收藏、轉發!

相關資料:

  • 《UNIX環境進階程式設計(第三版)》
  • 《高品質嵌入式Linux C程式設計》
  • https://zhuge.blog.csdn.net/article/details/123783374
  • https://blog.csdn.net/jinking01/article/details/112185115

如果文章對你有幫助,麻煩幫忙點贊、收藏、轉發,謝謝!

私信回複【嵌入式書籍】,可擷取部落客精心整理的嵌入式電子書一份

繼續閱讀