天天看點

Linux多線程之建立、終止、等待和分離

線程建立函數:

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void*), void *arg);
參數
thread:傳回線程ID。
attr:設定線程的屬性,attr為NULL表⽰示使⽤用預設屬性。
start_routine:是個函數位址,線程啟動後要執⾏行的函數。
arg:傳給線程啟動函數的參數。
傳回值:成功傳回;失敗傳回錯誤碼。
           

一般用pthread_create函數建立新線程。下面是利用pthread_create函數建立新線程的代碼:

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
void *thread_run(void *arg)
{
    while()
    {
        printf("I am a thread!\n");
        sleep();
    }
}
int main(void)
{
    pthread_t tid;
    pthread_create(&tid, NULL, thread_run, NULL);
    while()
    {
        printf("I am thread group!\n");
        sleep();
    }

    return ;
}
           

代碼中兩個線程都每隔一秒向螢幕列印字元串,來看看執行效果:

Linux多線程之建立、終止、等待和分離

也可以取得線程的線程ID:

#include <pthread.h>
pthread_t pthread_self(void);
傳回值:調用線程的ID。
           

這裡取得線程庫pthread範圍的線程ID,傳回值為pthread_t類型資料, pthread_t 的具體類型取決于實作,Linux的NPTL實作中,pthread_t類型的線程ID,本質上是一個程序位址空間的位址。

也可以用:

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
pid_t tid;
tid = syscall(SYS_gettid);
           

這裡取得核心級線程的線程ID,即pid。

可以将上面的代碼稍微改一下:

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#define _GNU_SOURCE
void *thread_run(void *arg)
{
    pthread_t tid = pthread_self();
    pid_t pid = syscall(SYS_gettid);
    while()
    {
        printf("I am a thread! user: %p, kernel: %d\n", tid, pid);
        sleep();
    }
}
int main(void)
{
    pthread_t tid;
    pthread_create(&tid, NULL, thread_run, NULL);
    pthread_t ptid = pthread_self();
    pid_t pid = syscall(SYS_gettid);
    while()
    {
        printf("I am thread group! user: %p, kernel: %d\n", ptid, pid);
        sleep();
    }

    return ;
}
           

運作代碼可以得到這樣的輸出:

Linux多線程之建立、終止、等待和分離

在多線程程序中,任何一個線程的異常終止都會導緻整個程序的退出,如果僅要求線程退出而不終止整個程序可用下面三種方法:

1. 從線程函數return。這種⽅方法對主線程不适⽤用,從main函數return相當于調⽤用exit。

2. 線程可以調⽤用pthread_ exit終⽌止⾃自⼰己。

3. ⼀一個線程可以調⽤用pthread_ cancel終⽌止同⼀一程序中的另⼀一個線程。

pthread_exit函數:

void pthread_exit(void *value_ptr);
功能:程序終止。
參數
value_ptr:value_ptr不要指向⼀個局部變量。
傳回值:⽆傳回值,跟程序一樣,線程結束的時候⽆無法傳回到它的調用者(自身)。
           

注意,這裡的傳回值必須是全局的或用malloc配置設定的,不能是函數棧上的指針,因為當其他線程得到這個傳回值時線程函數已經退出了。

pthread_cancel函數:

int pthread_cancel(pthread_t thread);
功能:取消一個執行中的線程。
參數
thread:線程ID。
傳回值:成功傳回;失敗傳回錯誤碼
           

要取得線程的傳回值,就要用到程序等待函數pthread_join:

int pthread_join(pthread_t thread, void **value_ptr);
功能:等待線程結束,調用該函數的線程将挂起等待,直到id為thread的線程終⽌。
參數
thread:線程ID
value_ptr:它指向⼀個指針,後者指向線程的傳回值
傳回值:成功傳回;失敗傳回錯誤碼
           

新建立的線程預設為是joinable的,即可結合的,當線程退出後必須程序pthread_join操作。因為線程退出後其空間不能自動釋放,必須顯式回收,如果不進行釋放的話新建立的線程不會複用剛剛的位址空間,這樣就造成了記憶體洩漏。

線程以不同的方法終止,通過pthread_join得到的終止狀态是不同的,總結如下:

1. 如果thread線程通過return傳回,value_ ptr所指向的單元⾥裡存放的是thread線程函數的傳回值。

2. 如果thread線程被别的線程調用pthread_ cancel異常終掉,value_ ptr所指向的單元裡存放的是常數PTHREAD_ CANCELED。

3. 如果thread線程是自己調用pthreadexit 終止的 ,valueptr所指向的單元存放的是傳給pthread_exit的參數。

4. 如果對thread線程的終止狀态不感興趣,可以傳NULL給value_ ptr參數。

代碼:

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#define _GNU_SOURCE
void *thread_run1(void *arg)
{
    printf("%s\n", (char*)arg);
    int *p = (int*)malloc(sizeof(int));
    *p = ;
    sleep();
    return (void*)p;
}
void *thread_run2(void *arg)
{
    printf("%s\n", (char*)arg);
    int *p = (int*)malloc(sizeof(int));
    *p = ;
    sleep();
    pthread_exit((void*)p);
}
void *thread_run3(void *arg)
{
    printf("%s\n", (char*)arg);
    while();
}
int main(void)
{
    pthread_t tid1, tid2, tid3;
    void *ret1, *ret2, *ret3;
    pthread_create(&tid1, NULL, thread_run1, "thread No1");
    pthread_create(&tid2, NULL, thread_run2, "thread No2");
    pthread_create(&tid3, NULL, thread_run3, "thread No3");
    //sleep(1);
    pthread_cancel(tid3);

    pthread_join(tid1, &ret1);
    printf("thread quit! thread id : %X, return code : %d\n", tid1, *(int*)ret1);
    free(ret1);
    pthread_join(tid2, &ret2);
    printf("thread quit! thread id : %X, return code : %d\n", tid2, *(int*)ret2);
    free(ret2);
    pthread_join(tid3, &ret3);
    if(ret3 == PTHREAD_CANCELED)
        printf("thread quit! thread id : %X, return code : PTHREAD_CANCELED\n", tid3);
    else
        printf("thread quit! thread id : %X, return code : NULL\n", tid3);


    return ;
}
           

運作結果:

Linux多線程之建立、終止、等待和分離

新建立的線程是可結合的,即需要調用pthread_join進行回收,而如果不需要線程函數的傳回值,那麼join就是一種負擔,此時我們可以将線程聲明為“分離”的,即當線程結束後系統自動釋放線程資源。

#include <pthread.h>
int pthread_detach(pthread_t thread);
功能:對線程thread進行分離。
傳回值:成功傳回;錯誤傳回錯誤編号。
線程也可以自己分離:
pthread_detach(pthread_self());
           

joinable與分離是沖突的,是以一個線程不能同時是joinable和分離的。

代碼:

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#define _GNU_SOURCE
void *thread_run(void *arg)
{
    pthread_detach(pthread_self());
    printf("%s\n", (char*)arg);
    return NULL;
}
int main(void)
{
    pthread_t tid;
    if(pthread_create(&tid, NULL, thread_run, "thread run") != )
    {
        printf("create thread errno\n");
        return ;
    }
    printf("I am thread group!\n");
    sleep();
    if(pthread_join(tid, NULL) == )
        printf("thread wait success\n");
    else
        printf("thread wait failed\n");

    return ;
}
           

先派生出一個線程,這個線程自己分離後執行後續操作,主線程調用pthread_join,如果等待成功則輸出“thread wait success”,失敗則輸出“thread wait failed”,來看運作結果:

Linux多線程之建立、終止、等待和分離

顯然派生出的線程自動進行線程分離,主線程等待失敗。

繼續閱讀