上午我說了循環創建多個線程,由於進程與線程是如此的相似,進程我們知道要回收,那么線程也自然要回收啦。我們接着看控制原語:
線程與共享
線程間共享全局變量!
【牢記】:線程默認共享數據段、代碼段等地址空間,常用的是全局變量。而進程不共享全局變量,只能借助mmap。
pthread_exit函數
將單個線程退出
void pthread_exit(void *retval); 參數:retval表示線程退出狀態,通常傳NULL
思考:使用exit將指定線程退出,可以嗎? 【pthrd_exit.c】
結論:線程中,禁止使用exit函數,會導致進程內所有線程全部退出。
在不添加sleep控制輸出順序的情況下。pthread_create在循環中,幾乎瞬間創建5個線程,但只有第1個線程有機會輸出(或者第2個也有,也可能沒有,取決於內核調度)如果第3個線程執行了exit,將整個進程退出了,所以全部線程退出了。
所以,多線程環境中,應盡量少用,或者不使用exit函數,取而代之使用pthread_exit函數,將單個線程退出。任何線程里exit導致進程退出,其他線程未工作結束,主控線程退出時不能return或exit。
另注意,pthread_exit或者return返回的指針所指向的內存單元必須是全局的或者是用malloc分配的,不能在線程函數的棧上分配,因為當其它線程得到這個返回指針時線程函數已經退出了。
總結exit、return、pthread_exit各自退出效果。
return:返回到調用者那里去。
pthread_exit():將調用該函數的線程退出
exit: 將進程退出。
pthread_join函數
阻塞等待線程退出,獲取線程退出狀態 其作用,對應進程中 waitpid() 函數。
int pthread_join(pthread_t thread, void **retval); 成功:0;失敗:錯誤號
參數:thread:線程ID (【注意】:不是指針);retval:存儲線程結束狀態。
對比記憶:
進程中:main返回值、exit參數-->int;等待子進程結束 wait 函數參數-->int *
線程中:線程主函數返回值、pthread_exit-->void *;等待線程結束 pthread_join 函數參數-->void **
參數 retval 非空用法。
調用該函數的線程將掛起等待,直到id為thread的線程終止。thread線程以不同的方法終止,通過pthread_join得到的終止狀態是不同的,總結如下:
-
如果thread線程通過return返回,retval所指向的單元里存放的是thread線程函數的返回值。
-
如果thread線程被別的線程調用pthread_cancel異常終止掉,retval所指向的單元里存放的是常數PTHREAD_CANCELED。
-
如果thread線程是自己調用pthread_exit終止的,retval所指向的單元存放的是傳給pthread_exit的參數。
-
如果對thread線程的終止狀態不感興趣,可以傳NULL給retval參數。
上面說的很清楚啦,所以回收線程的代碼實現也很簡單啦:
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
int x;
int y;
char p[128];
}my_func;
void *func(void*arg)
{
my_func *mf = (my_func*)malloc(sizeof(my_func));
int i = (int)arg;
mf->x = i;
mf->y = i * i;
strcpy(mf->p, "my lover is dandan.");
printf("我是第%d個線程,我的線程ID是%lu\n", i + 1, pthread_self());
pthread_exit(mf);
}
int main(void)
{
pthread_t pth[5];
my_func *mf;//這里是指針啊,是指針啊,不是一個實例啊
for (int i = 0; i != 5; i++)
{
pthread_create((pth + i), NULL, func, (void*)i);
if (!pthread_join(pth[i], (void**)&mf))
{
printf("回收線程成功!線程ID:%lu。\n", pth[i]);
printf("my_func中的x = %d, y = %d, p = %s。\n\n", mf->x, mf->y, mf->p);
}
else
printf("失敗!\n");
}
return 0;
}
主要是各種轉化要注意,還有的是:my_func *mf;//這里是指針啊,是指針啊,不是一個實例啊 這句話很重要啊,我就說為什么我的mf輸出像是沒有經過初始化和賦值操作的。還有,不用擔心我沒有free而造成內存泄漏,pthread_exit函數是把這些考慮好了的。還有我的pthread_exit是傳參數進去的,主要是想獲取一下線程退出的狀態。是不是和wait很類似?