未分離線程
在我們使用默認屬性創建一個線程的時候,線程是 joinable 的。 joinable 狀態的線程,必須在另一個線程中使用 pthread_join() 等待其結束, 如果一個 joinable 的線程在結束后,沒有使用 pthread_join() 進行操作, 這個線程就會變成"僵屍線程"。每個僵屍線程都會消耗一些系統資源, 當有太多的僵屍線程的時候,可能會導致創建線程失敗。
下面是一個創建僵屍線程的例子:
void* start_thread(void * arg) { //這個線程什么也不做,直接退出 pthread_exit(NULL); return NULL; } int create_pthread(int n) { int try = 0; int i = 0; int err = 0; for(i = 0; i < n && try < 3; i++) { //當創建線程失敗的時候,嘗試3次 pthread_t pt; err = pthread_create(&pt, NULL, start_thread, NULL); if(0 != err) { if(EAGAIN == err) { printf("errno : [EAGAIN]\n"); } sleep(2); try ++; } } printf("create [%d] threads\n", i); return err; } int main(int argc, char * argv[]) { int n = 1 << 20;//最多創建 1M 個線程 create_pthread(n); return 0; }
上面代碼是創建僵屍線程的主要部分,把上面代碼編譯執行后, 輸出的結果是什么呢:
errno : [EAGAIN] errno : [EAGAIN] errno : [EAGAIN] create [32754] threads
在上面的例子中,每個線程開始后,什么也不做,立刻就結束。 最后在我的環境下,只能創建 32754 個額外線程。
分離線程
當線程被設置為分離狀態后,線程結束時,它的資源會被系統自動的回收, 而不再需要在其它線程中對其進行 pthread_join() 操作。
下面我們對代碼做一些修改,讓每個線程在開始執行后,進行線程的分離, 然后看看執行后的結果。
void* start_thread(void * arg) { //注意,這里執行了線程的分離操作。線程分離也可以在線程創建的時候, //在屬性里面設置 pthread_detach(pthread_self()); pthread_exit(NULL); return NULL; } int create_pthread(int n) { int try = 0; int i = 0; int err = 0; for(i = 0; i < n && try < 3; i++) { pthread_t pt; err = pthread_create(&pt, NULL, start_thread, NULL); if(0 != err) { if(EAGAIN == err) { printf("errno : [EAGAIN]\n"); } sleep(2); try ++; } } printf("create [%d] threads\n", i); return err; } int main(int argc, char * argv[]) { int n = 1 << 20; create_pthread(n); return 0; }
執行結果如下:
create [1048576] threads
可以看到,最后成功創建了 1M 個線程。而沒有進行 pthread_join() 操作的時候,最多創建創建了 32754 個線程,原因就是沒有 detach 的線程,在其結束后,一些系統分配給它的資源還在被占用, 沒有被回收,導致無法再創建新的線程了。而 detach 的線程,在其結束后, 它的資源會被系統進行回收,然后進行再利用。所以這次才能創建 1M 的線程。
join 后的線程
在 man pthread_create 手冊中,可以看到關於一個線程的 detachd 狀態和 joinable 狀態的描述。其中說到,一個線程或者是 detachd 的狀態,這樣它結束后,資源會被系統 回收;或者是 joinable 狀態,並且在另一個線程里面被 pthread_join() 等待,pthread_join() 會回收它的資源。
我們可以再做個實驗,在創建完了一個 joinable 的線程后再使用 pthread_join() 等待它的結束, 回收它的資源,看看最多可以創建多少個線程。
void* start_thread(void * arg) { pthread_exit(NULL); return NULL; } int create_pthread(int n) { int try = 0; int i = 0; int err = 0; for(i = 0; i < n && try < 3; i++) { pthread_t pt; err = pthread_create(&pt, NULL, start_thread, NULL); if(0 != err) { if(EAGAIN == err) { printf("errno : [EAGAIN]\n"); } sleep(2); try ++; } void *st = NULL; //這里等待一個線程的結束,並回收其資源 err = pthread_join(pt, &st); if(0 != err) { printf("errno = [%d]\n", err); sleep(2); try ++; } } printf("create [%d] threads\n", i); return err; } int main(int argc, char * argv[]) { int n = 1 << 20; create_pthread(n); return 0; }
執行結果:
create [1048576] threads
最后也是創建了 1M 個線程。
結論就是,在我們編寫多線程應用的時候,或者把一個線程的屬性設置為 detachd 的狀態,讓系統來回收它的資源;或者是 joinable 狀態,這樣就可以使用 pthread_join() 來阻塞的等待一個線程的結束,並回收其資源,並且pthread_join() 還會得到線程退出后的返回值,來判斷線程的退出狀態 。
同步地址:https://www.fengbohello.top/archives/linux-pthread-detach