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