Linux多線程編程並傳遞多個參數實例
你定義了一個函數指針。名字叫 start_routine 。 這個函數的返回值是void *(一個指針) 參數是void *(一個指針) 一般這種寫法最好用typedef void* (start_routine)(void ) ,然后用start_routine當作一種類型來使用。 如: start_routine pfoo;調用的時候: *pfoo(p);
例子詳細解析:
一. pthread_create()與pthread_join()函數
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
1. pthread_join函數作用
pthread_join函數作用是在一個線程中以阻塞的方式等待另一個線程(線程標識符為thread)的退出。如果等待的進程已經結束,那么該函數會立即返回。
retval是用戶定義的指針,用來存儲被等待線程的返回值。
返回值: 0 -- 成功,失敗 -- 錯誤號errno
2. pthread_join的應用
使一個線程等待另一個線程的結束代碼中如果沒有pthread_join主線程會很快結束,從而從而合整個進程線束,從而使創建的線程沒有機會執行就結束了,在主線程加入pthread_join后,主線程會阻塞等待直到(被等待的)線程結束后,主線程自己才結束,從而使創建的線程有機會執行。
3. 一個線程不能被多個線程等待,否則第一個接收到信號的線程成功返回,其余調用 pthread_join 的線程則返回錯誤代碼ESRCH。
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
1. pthread_create函數的作用
創建一個線程,成功時返回0,錯誤時返回errno。
thread:被創建線程的標識符,pthread_join使用這個標識符來等待該線程的結束。
attr: 設置線程的屬性,可以為NULL
第三個參數是線程函數的入口地址
arg: 傳遞給線程的參數,當要傳遞給線程的參數有多個時,可以使用結構體.
具體例子
例子中要請注意的地方:
void * thread1_func( void *arg )
{
...
return (void*)123;
}
void *thread2_func( void *arg )
{
...
return (void*)456;
}
1. 在線程函數中thread1_func()和thread2_func()中,最后一句return語句中,對返回的值要進行類型轉換(轉換成(void *)),返回值的類型要與線程函數的聲明和定義的返回值類型一致。
2. 兩個線程函數的返回值均為一個指針(把一個整數轉換成(void*)返回)。該指針存儲在pthread_join()的第三個參數中,在這兩個函數中等價於
&thread1_return = thread1_func((void*)&arg1);
&thread2_return = thread2_func((void*)&arg2);
其中,&thread1_return是pthread_join的第二個參數,在前面函數解析中說過,pthread_join(phtread_t thread, void **retval)函數的第二個參數retval可以存儲線程的返回值。該返回值直接存儲在&thread1_return和&thread2_return,此時thread1_return和thread2_return值就是線程1和線程2函數的返回值(void *)類型。所以(int)thread1_return和(int)thread2_return就是該函數的返回值內容。由定義(void *thread1_return, *thread2_return)可以看出,thread1_return和thread2_return中兩個指針,也就是說,這兩個指針所存儲的地址已經被兩個線程的返回值所覆蓋。
Linux多線程編程 —— 線程調用函數時傳入參數
當調用pthread_create 第三個和第四個參數不為空時,要注意第四個參數的傳遞方法
一段流行的代碼:
#include <iostream>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 10
void* say_hello(void* args)
{
int i = *((int*)args[c1] ); /*[c1]//對傳入的參數進行強制類型轉換,由無類型指針變為整形數指針,然后再讀取;*/
cout << "hello in " << i << endl;
}
int main()
{
pthread_t tids[NUM_THREADS];
cout << "hello in main..." << endl;
for(int i = 0; i < NUM_THREADS; ++i)
{
int ret = pthread_create(&tids[i], NULL, say_hello, (void *)&i[c1] ); /* [c1]//傳入的時候必須強制轉換為void* 類型,即無類型指針*/
cout << "Current pthread id =" << tids[i] << endl[c2] ; /* [c2]//這里學會使用tids數組打印創建的進程id信息;*/
if (ret != 0)
{
cout << "pthread_create error: error_code=" << ret << endl;
}
}
pthread_exit(NULL);
}
程序運行結果:
hello in main...
Current pthread id =140210171029248
Current pthread id =140210162636544
Current pthread id =140210154243840
Current pthread id =140210145851136
Current pthread id =140210137458432
Current pthread id =140210129065728
Current pthread id =140210120673024
Current pthread id =140210112280320
hello in 6
hello in 6
hello in 6
hello in 6
hello in hello in 6
hello in 6
Current pthread id =140210103887616
Current pthread id =140210095494912
hello in 10
hello in 10
hello in 10
顯然與預期不一致,我猜想原因:我們 pthread_create的時候,傳入i的方式是 取i的地址指針再強制轉換為無類型指針,然后在sayhello函數里,再把無類型指針轉換為int類型指針(這樣做是為了滿足phtread_create的參數設定),再通過 * 號取值。
也就是說,我們是傳遞了一個 i 的指針的拷貝給了sayhello函數。這意味着,每個線程通過這個指針取值時,訪問的都是同一塊內存。為什么會出現上面的結果呢?
注意打印的信息中:
先打印了7個主線程(main函數中)Current pthread id 信息,才開始打印出其它線程中的hello in信息。說明主線程 pthread_create(&tids[i], NULL, say_hello, (void *)&i)之后,這個線程並沒有立即獲得操作系統資源;運行的機會仍能被握在主線程手上,直到第8個printf之后,其它線程才獲得時間片,得到了運行的機會,但此時儲存變量i 的這塊內存里面的東西已經變了, i的值已經不是當時線程被create時它們的那個i了,所以再通過指針解引用去取當然拿不到預期的那個值!
流行的一種做法:把 i 賦值給一個數組保存,這樣就避免了在同一地址取值。
#include <iostream>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 10
void* say_hello(void* args)
{
cout << "hello in thread " << *((int *)args) << endl;
}
int main()
{
pthread_t tids[NUM_THREADS];
int indexes[NUM_THREADS];//用個數組來保存i的值,就不會變了
for(int i = 0; i < NUM_THREADS; ++i)
{
indexes[i] = i;//先保存i的值,再調用線程就不會出現問題了
int ret = pthread_create( &tids[i], NULL, say_hello, (void *)&(indexes[i]) );
if (ret != 0)
{
cout << "pthread_create error: error_code=" << ret << endl;
}
}
for (int i = 0; i < NUM_THREADS; ++i)
pthread_join(tids[i], NULL);
}
運行結果:
hello in main..
Current pthread id = 140457201362688
Current pthread id = 140457192969984
Current pthread id = 140457184577280
Current pthread id = 140457176184576
Current pthread id = 140457167791872
Current pthread id = 140457159399168
Current pthread id = 140457151006464
Current pthread id = 140457142613760
Current pthread id = 140457134221056
Current pthread id = 140457125828352
hello in 3
hello in 4
hello in 5
hello in 6
hello in 7
hello in 8
hello in 9
hello in 2
hello in 1
hello in 0
然后我發現還有一種寫法也可以: 直接把 i 的值 強制轉換成 無類型指針 傳過去,然后再 在 被調函數里 把無類型指針參數強制轉換回來。 這樣實際上傳遞的是i的不同的拷貝,每個線程都拿到一個(void*) i(以參數形式),而這個(void *) i顯然是各不一致的。所以也可以達到目的,但這只能算是一個小技巧吧,毫不講理的指針轉換……也只有C/C++能做到。
注意,在64 位Linux機里,指針是8個字節,int是4個字節,long也是8個字節,所以必須轉換成long型,否則會因丟失精度而報錯。從這里也可以看出來,第一個程序結果和預期不一致 不是因為 i 的值和預期不一致,而是 i 的地址出問題了。
#include <iostream>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 10 //線程數
void* say_hello( void* args )
{
cout << "hello in " << long(args) << endl;
}
int main()
{
pthread_t tids[NUM_THREADS]; //線程id
cout << "hello in main.." << endl;
for( int i = 0; i < NUM_THREADS; ++i )
{
int ret = pthread_create( &tids[i], NULL, say_hello, (void*)i );
//直接把i的值傳過去
cout << "Current pthread id = " << tids[i] << endl;
//用tids數組打印創建的進程id信息
if( ret != 0 ) //創建線程成功返回0
{
cout << "pthread_create error:error_code=" << ret << endl;
}
}
for( int i = 0; i < NUM_THREADS; ++i )
{
pthread_join(tids[i],NULL);
}
}
運行結果:
hello in main..
Current pthread id = 140406320125696
Current pthread id = 140406311732992
Current pthread id = 140406303340288
Current pthread id = 140406294947584
Current pthread id = 140406286554880
Current pthread id = 140406278162176
Current pthread id = 140406269769472
Current pthread id = 140406261376768
Current pthread id = 140406252984064
Current pthread id = 140406244591360
hello in 6
hello in 7
hello in 8
hello in 9
hello in 5
hello in 4
hello in 3
hello in 2
hello in 1
hello in 0
轉自:https://www.cnblogs.com/blankqdb/articles/2651029.html