建立線程: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() 函數會結束當前進程。若是當前線程是能夠被 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 退出線程的例子:
#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 的說明放在其它地方了。
先看一下函數原型:
#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) 中列出的函數就是或者是取消點。
當一個取消請求起做用時,下面的步驟會按順序發生。
以上的步驟會異步的執行,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