linux多執行緒相關的API-(1)--建立/退出/加入/分離等
注意:多執行緒相關的程式碼,在編譯時必須加-lpthread或者-pthread選項,例如:
gcc thread_test.c -o test_exe -pthread
一、建立執行緒:pthread_create
原型:
int pthread_create(pthread_t *tid,
const pthread_attr_t *attr,
(void*)(*start_rtn)(void* arg),
void *arg);
形參:tid輸出所成功建立的新執行緒的執行緒ID,tid=thread id;
attr設定所建立的執行緒引數,傳入NULL意味著使用預設引數;
start_rtn執行緒入口函式,函式格式形如: void *thread_entry(void *para);
arg建立新執行緒時,要傳給入口函式的引數;
注意:通過本函式把引數arg傳給新執行緒時,是需要一定時間的(確切的說,是建立執行緒這一過程較為耗時),那麼從執行完本函式開始,一直到新執行緒被建立完成,且新執行緒讀完arg引數之前,主執行緒中實參arg指向的內容不允許變,否則,新執行緒讀到的啟動引數就不對了,這一問題主要出現在類似這種程式碼中:
pthread_t tid[5]; for( i = 0; i < 5; i++) { stat = pthread_create(&tid[i], NULL, test_func, &i); ````//其餘程式碼 }
感覺上,建立的5個執行緒,會在各自的入口函式中,依次收到pthread_create的第4個實參:0、1、2、3、4,而實際上,這5個執行緒收到的卻都是5,原因就在於,程序尚未讀到這個第四實參,i的值就被改變了。
這一問題一般有3個解決方法:
① 本函式返回後,sleep一段時間,目的是執行緒建立完畢,且讀完arg引數,
② 為每一個執行緒用不同的變數傳遞引數,而不是跟這個例子似的,用同一個變數i;
③ 這種方法僅適用於傳遞的資料的位元組數≤sizeof(指標)時,換句話說就是,本來傳參是通過傳遞個地址給執行緒,讓執行緒從這個地址中讀出真正的引數,顯然,這也是引發上述問題的根源,這個地址處的值可能會改變。但是,pthread_create傳遞的第4實參,指標本身的值不會變,也即,我們可以投機取巧,把這個指標本身的值作為引數傳給新執行緒,這個值無論如何不會出錯的。也即,線上程入口函式void* start_rtn(void* arg)中,讀*arg的值可能會變,*arg的值可能會被主執行緒給修改,但是arg本身的值,是不會變的。
二、執行緒退出:pthread_exit
原型:void pthread_exit(void* retval);
功能:立即退出執行緒,如果退出時還想返回一些資料供父執行緒處理/使用,那麼這些資料可以通過函式形參retval傳出,傳出的值可通過pthread_join函式獲得。
注意:
1、我們傳出的是資料的地址,而不是資料本身,因此資料本身必須是靜態的(如果多個執行緒都用同一個入口函式,這方法就不行了)、或者從記憶體堆動態分配的,而決不能是函式中的動態臨時變數,因為一旦執行緒入口函式返回,那麼棧中的資料值就消失/不可用了
2、線上程的入口函式中執行return p;和執行pthread_exit(p);相同點是:都可以退出執行緒,都可以傳出返回值,而且返回值都可以被pthread_join捕獲。
區別在於:return p只能在入口函式中退出執行緒,而pthread_exit(p)可以在入口函式的子函式中退出執行緒;return p不會觸發執行緒清理工作,pthread_exit(p)可以觸發執行緒清理工作,詳情請參考另一篇博文:執行緒取消與清理。
三、阻塞地等待某個執行緒退出pthread_join
原型:int pthread_join(pthread_t tid, void **retval);
功能:阻塞地等待某個執行緒退出,另外注意:父執行緒建立子執行緒之後,父執行緒會維護一些子執行緒的資訊,只有顯式地呼叫了該函式,父程序才會在子執行緒結束後清理所維護的子執行緒資訊,換句話說,如果父執行緒不呼叫該函式,那麼大量的子執行緒資訊得不到釋放,可能會造成堆溢位,除非:我們把建立的子執行緒與父執行緒分離開,這樣子執行緒的資訊在子執行緒結束後自動釋放,分離執行緒所用的函式為:pthread_detach(pthread_t tid);
形參:tid 要等待的執行緒id;
retval為執行緒退出時,傳出的值的指標的指標
//編譯:gcc thread_basic.c -o test_exe -pthread
#include <limits.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
void *thread_entry(void* arg)
{
int arg_val = *(int*)arg;
printf("child thread start with para: %d\n", arg_val);
int *p_out = malloc(sizeof(int));//申請的空間用於傳出執行緒的退出值
//static int data_out = 0;
//int *p_out = &data_out;//區域性靜態空間也可用來傳出退出值
*p_out = arg_val + 1;
pthread_exit(p_out); //在入口函式中,本行程式碼等價於 return p_out;
return p_out;//這一行實際上執行不到,執行緒已經被pthread_exit給退出了
}
int main()
{
int status;//執行緒操作函式的返回值
pthread_t tid;//子執行緒ID
int arg = 2;//傳給子執行緒入口函式的實參
int *p_recv_out;//pthread_join接收子執行緒的返回值
//建立子執行緒
status = pthread_create(&tid, NULL, thread_entry, &arg);
if(0 == status)
{
printf("creat thread ok, tid = %ld\n", tid);
}
else
{
printf("creat thread failed, error info: %s\n", strerror(errno));
return -1;
}
//等待子執行緒結束,並接收子執行緒的結束值
status = pthread_join(tid, (void**)&p_recv_out);
if(0 == status)
{
printf("child thread quit normally\n");
printf("parent thread received exit data: %d\n", *p_recv_out);
free(p_rec
執行結果: