POSIX 線程的建立與退出

前言

建立線程:react

pthread_create()

退出線程:linux

pthread_exit()return
pthread_cancel()

線程的建立

使用多線程,首先就須要建立一個新線程。那麼線程是如何被建立的呢,是用下面這個函數建立的。多線程

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

//Compile and link with -pthread
 

建立函數的四個參數的意義分別以下:app

thread :用來返回新建立的線程的 ID,這個 ID 就像身份證同樣,指定了這個線程,能夠用來在隨後的線程交互中使用。

attr   : 這個參數是一個 pthread_attr_t 結構體的指針,用來在線程建立的時候指定新線程的屬性。若是在建立線程時,這個參數指定爲 NULL, 那麼就會使用默認屬性。

start_routine :這個就是新線程的入口函數,當新線程建立完成後,就從這裏開始執行。

arg :arg 參數就是要傳遞給 start_routine 的參數。

 

返回值:若是函數執行成功,則返回 0,若是執行失敗,則返回一個錯誤碼。異步

錯誤碼:函數

    EAGAIN :資源不足以用來建立一個新的線程,或者是達到了系統對線程數量的限制,請參考 setrlimit(2) 和 /proc/sys/kernel/threads-max 
    EINVAL :不可用的 attr 
    EPERM  :沒有權限設置 attr 中的一下屬性或者執行時序策略。

 

下面就是調用 pthread_create() 函數建立線程的一個例子:this

#include <stdio.h>
#include <pthread.h>
#include <errno.h>

void *
thread_start(void *arg) {
    if(NULL == arg) {
        printf("[%u] : arg is NULL\n", (unsigned int)pthread_self());
        return NULL;
    }
    char * p = (char*)arg;
    printf("[%u] : arg = [%s]\n", (unsigned int)pthread_self(), p);

    return NULL;
}

int main() {
    pthread_t pt;
    int errn = pthread_create(
        &pt,         //用來返回新建立的線程的 ID
        NULL,        //使用默認的線程屬性
        thread_start,//新線程從這個函數開始執行
        "hello");    //傳遞給新建立的線程的參數

    if(0 != errn) {
        printf("error happend when create pthread, errno = [%d]\n", errn);
        if(EAGAIN == errn) {
            printf("Insufficient  resources\n");
        } else if (EINVAL == errn) {
            printf("Invalid settings in attr\n");
        } else if (EPERM == errn)  {
            printf("No permission\n");
        } else {
            printf("An error number that unexpected [%d], when create pthread\n", errn);
        }

        return -1;
    } else {
        printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
    }
    void *r = NULL;
    errn = pthread_join(pt, &r);
    if(0 != errn) {
        printf("error happend when join, errno = [%d]\n", errn);
        if(EDEADLK == errn) {
            printf("A  deadlock  was  detected; or thread specifies the calling thread\n");
        } else if (EINVAL == errn) {
            printf("thread is not a joinable thread, or Another thread is already waiting to join with this thread\n");
        } else if (ESRCH == errn) {
            printf("No thread with the ID thread could be found\n");
        } else {
            printf("An error number that unexpected [%d], when join\n", errn);
        }
        return -1;
    } else {
        printf("thread [%u] over\n", (unsigned int)pt);
    }

    return 0;
}

 

接下來編譯並運行,看看結果:spa

gcc -g -c -o pthread_create.o pthread_create.c -Wall -I./
gcc -g -o pthread_create pthread_create.o -Wall -I./ -lpthread

create thread success, threadid : [1243236096]
[1243236096] : arg = [hello]
thread [1243236096] over

 

看起來執行成功了。下面再來看看一個線程的退出過程。操作系統

線程的退出

從上面的例子中,咱們也能夠看出,線程的入口,也就是一個函數,函數可使用 return 進行退出, 那麼在線程中,也是經過 return 進行退出的嗎? 答案是,可使用 return ,可是若是但願線程在退出的時候, 可以執行更多的動做,就不能使用 return 直接退出了,那麼該怎樣退出呢,可使用 pthread_exit() 函數, 或者使用pthread_cancel() 函數。命令行

這兩個函數的原型以下:

#include <pthread.h>

int pthread_cancel(pthread_t thread);   //向指定的線程發送取消請求
void pthread_exit(void *retval);        //結束調用者線程

//Compile and link with -pthread

 

使用 pthread_exit() 退出線程

pthread_exit() 函數會結束當前進程。若是當前線程是能夠被 join 的,則會經過參數 retval 返回一個值給同一個進程裏面的另外一個使用pthread_join(3) 函數的線程。

全部使用pthread_cleanup_push(3)函數壓入棧的清理函數,都會被彈出並調用, 調用順序是入棧時的反向順序。若是線程有什麼特別指定的數據,那麼在全部的清理函數執行結束後, 會有適當的函數被調用,來析構這些數據,調用順序不固定。

當一個線程終止後,進程內共享的資源(例如互斥信號量、條件變量、信號量以及文件描述符) 不會被釋放。而且使用atexit(3)函數註冊的函數也不會被調用。

當進程內的最後一個線程終止後,進程也就終止了,就像調用了exit(3)函數同樣,而且參數是0. 這時候,進程內的共享資源就會被釋放,而且使用 atexit(3) 函數註冊的函數, 也會被調用。

下面來看一下 pthread_exit() 函數的一個例子:

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

void handlers(void *arg) {
    if(NULL != arg) {
        printf("%s() : [%s]\n", __func__, (char*)arg);
    } else {
        printf("%s()\n", __func__);
    }
}

void *
thread_start(void *arg) {
    printf("hello, this is thrad [%u]\n", (unsigned int)pthread_self());
    pthread_cleanup_push(handlers, "one");
    pthread_cleanup_push(handlers, "two");
    pthread_cleanup_push(handlers, "three");

    //注意,這裏執行了 pthread_exit() 函數
    pthread_exit("he~he~");

    pthread_cleanup_pop(1);
    pthread_cleanup_pop(2);
    pthread_cleanup_pop(3);

    return NULL;
}

int main() {
    pthread_t pt;
    int errn = pthread_create(&pt, NULL, thread_start, NULL);

    if(0 != errn) {
        printf("error [%d], when create pthread\n", errn);
        return -1;
    } else {
        printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
    }
    void *r = NULL;
    errn = pthread_join(pt, &r);
    if(0 != errn) {
        printf("error happend when join, errno = [%d]\n", errn);
        return -1;
    } else {
        printf("thread [%u] over\n", (unsigned int)pt);
    }
    if(NULL != r) {
        printf("thread return : [%s]\n", (const char*)r);
    }

    return 0;
}

 

編譯並運行:

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

void handlers(void *arg) {
    if(NULL != arg) {
        printf("%s() : [%s]\n", __func__, (char*)arg);
    } else {
        printf("%s()\n", __func__);
    }
}

void *
thread_start(void *arg) {
    printf("hello, this is thrad [%u]\n", (unsigned int)pthread_self());
    pthread_cleanup_push(handlers, "one");
    pthread_cleanup_push(handlers, "two");
    pthread_cleanup_push(handlers, "three");

    //注意,這裏執行了 pthread_exit() 函數
    pthread_exit("he~he~");

    pthread_cleanup_pop(1);
    pthread_cleanup_pop(2);
    pthread_cleanup_pop(3);

    return NULL;
}

int main() {
    pthread_t pt;
    int errn = pthread_create(&pt, NULL, thread_start, NULL);

    if(0 != errn) {
        printf("error [%d], when create pthread\n", errn);
        return -1;
    } else {
        printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
    }
    void *r = NULL;
    errn = pthread_join(pt, &r);
    if(0 != errn) {
        printf("error happend when join, errno = [%d]\n", errn);
        return -1;
    } else {
        printf("thread [%u] over\n", (unsigned int)pt);
    }
    if(NULL != r) {
        printf("thread return : [%s]\n", (const char*)r);
    }

    return 0;
}

 

使用 return 退出線程

先來看一個使用 return 退出線程的例子:

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

void *
thread_start(void *arg) {
    printf("hello, this is thread [%u]\n", (unsigned int)pthread_self());

    return "ok";
}

int main() {
    pthread_t pt;
    int errn = pthread_create(&pt, NULL, thread_start, NULL);

    if(0 != errn) {
        printf("error [%d], when create pthread\n", errn);
        return -1;
    } else {
        printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
    }
    void *r = NULL;
    errn = pthread_join(pt, &r);
    if(0 != errn) {
        printf("error happend when join, errno = [%d]\n", errn);
        return -1;
    } else {
        printf("thread [%u] over\n", (unsigned int)pt);
    }
    if(NULL != r) {
        printf("thread return : [%s]\n", (const char*)r);
    }

    return 0;
}

 

編譯並運行:

gcc -g -c -o pthread-return.o pthread-return.c -Wall -I./
gcc -g -o pthread-return pthread-return.o -Wall -I./ -lpthread

./pthread-return 
create thread success, threadid : [722429696]   //主線程打印的信息
hello, this is thread [722429696]               //新建立的線程打印的信息
thread [722429696] over                         //主線程打印的信息
thread return : [ok]                            //主線程打印的信息,其中[ok]爲新建立的線程打印的信息

 

既然 return 和 pthread_exit() 函數都是結束線程,並返回數據,那麼它們之間的區別是什麼呢?

區別就在於,使用 return 退出線程的時候,不會執行線程使用 pthread_cleanup_push(3) 註冊的清理函數。 能夠再寫一個例子,看看效果。

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

void handlers(void *arg) {
    if(NULL != arg) {
        printf("%s() : [%s]\n", __func__, (char*)arg);
    } else {
        printf("%s()\n", __func__);
    }
}

void *
thread_start(void *arg) {
    printf("hello, this is thrad [%u]\n", (unsigned int)pthread_self());
    pthread_cleanup_push(handlers, "one");
    pthread_cleanup_push(handlers, "two");
    pthread_cleanup_push(handlers, "three");

    //注意,這裏執行了 return
    return "he~he~";

    pthread_cleanup_pop(1);
    pthread_cleanup_pop(2);
    pthread_cleanup_pop(3);

    return "ok";
}

int main() {
    pthread_t pt;
    int errn = pthread_create(&pt, NULL, thread_start, NULL);

    if(0 != errn) {
        printf("error [%d], when create pthread\n", errn);
        return -1;
    } else {
        printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
    }
    void *r = NULL;
    errn = pthread_join(pt, &r);
    if(0 != errn) {
        printf("error happend when join, errno = [%d]\n", errn);
        return -1;
    } else {
        printf("thread [%u] over\n", (unsigned int)pt);
    }
    if(NULL != r) {
        printf("thread return : [%s]\n", (const char*)r);
    }

    return 0;
}

 

編譯並運行:

gcc -g -c -o pthread-return.o pthread-return.c -Wall -I./
gcc -g -o pthread-return pthread-return.o -Wall -I./ -lpthread

./pthread-return 
create thread success, threadid : [4185097984]
hello, this is thrad [4185097984]
thread [4185097984] over
thread return : [he~he~]

 

能夠看出,確實沒有執行清理函數,爲何呢?

由於pthread_cleanup_push(3) 和 pthread_cleanup_pop() 是使用宏實現的。 在 pthread_cleanup_push() 和 pthread_cleanup_pop() 之間,是一個大個的 do{}while(0), 遇到 return 固然就直接退出啦。 具體的實現方式請看這裏, 由於本文只講述一下線程的建立和退出, 因此 pthread_cleanup_push 和 pthread_cleanup_pop 的說明放在其它地方了。

使用 pthread_cancel() 退出線程

先看一下函數原型:

#include <pthread.h>

int pthread_cancel(pthread_t thread);

//Compile and link with -pthread.

 

其中的 thread 參數就是目的線程的線程ID

pthread_cancel() 函數會給 thread 指定的線程發送一個取消請求。 至於目標線程是否以及合適對這個請求進行反應,則視目標線程的兩個屬性而定: 取消屬性的 state 和 type

一個線程的取消屬性的 state 由 pthread_setcancelstate(3) 函數來設置, 能夠是 enabled (一個新建立的線程的默認方式就是 enabled)或者 disabled。 若是一個線程的取消屬性設置了 disabled ,那麼對着個線程發送的取消請求會一直存在, 直到線程恢復了取消屬性的設置。若是一個線程的取消屬性設置了 enabled , 那麼取消屬性的 type 就由取消消息什麼何時到來而決定了。

一個線程的取消類型(type)由 pthread_setcanceltype(3) 函數來設置。 能夠是異步的,也能夠是延緩的。異步取消屬性的意味着線程任什麼時候間均可以被取消 (一般是當即被取消,但操做系統不保證這一點)。延緩取消是說,取消操做會被延遲, 直到線程接下來的調用的函數是個取消點。在 pthreads(7) (Linux 命令行中執行 man 7 pthreads) 中列出的函數就是或者是取消點。

當一個取消請求起做用時,下面的步驟會按順序發生。

    1. 取消清理函數會被出棧並被執行。
    1. 線程相關數據會被析構,順序不肯定。
    1. 線程終止。

以上的步驟會異步的執行,pthread_cancel() 函數的返回狀態會指出取消請求是否成功的發給了制定的線程。

在一個被取消的線程終止後,使用 pthread_join(3) 函數 join 時,會獲得線程的結束狀態爲 PTHREAD_CANCELED 。 join 一個線程是知道這個取消操做是否完成的惟一方法。

下面是 man pthread_cancel 手冊中的一段示例代碼:

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

#define handle_error_en(en, msg) \
    do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

    static void *
thread_func(void *ignored_argument)
{
    int s;

    /*  Disable cancellation for a while, so that we don't
     *                immediately react to a cancellation request */

    s = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_setcancelstate");

    printf("thread_func(): started; cancellation disabled\n");
    sleep(5);
    printf("thread_func(): about to enable cancellation\n");

    s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_setcancelstate");

    /*  sleep() is a cancellation point */

    sleep(1000);        /*  Should get canceled while we sleep */

    /*  Should never get here */

    printf("thread_func(): not canceled!\n");
    return NULL;
}

    int
main(void)
{
    pthread_t thr;
    void *res;
    int s;

    /*  Start a thread and then send it a cancellation request */

    s = pthread_create(&thr, NULL, &thread_func, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_create");

    sleep(2);           /*  Give thread a chance to get started */

    printf("main(): sending cancellation request\n");
    s = pthread_cancel(thr);
    if (s != 0)
        handle_error_en(s, "pthread_cancel");

    /*  Join with thread to see what its exit status was */

    s = pthread_join(thr, &res);
    if (s != 0)
        handle_error_en(s, "pthread_join");

    if (res == PTHREAD_CANCELED)
        printf("main(): thread was canceled\n");
    else
        printf("main(): thread wasn't canceled (shouldn't happen!)\n");
    exit(EXIT_SUCCESS);
}

 

編譯並運行 :

./pthread_cancel 
thread_func(): started; cancellation disabled
main(): sending cancellation request
thread_func(): about to enable cancellation
main(): thread was canceled

 

同步地址:https://www.fengbohello.top/archives/linux-pthread-lifecycle

相關文章
相關標籤/搜索