Linux多线程编程 —— 线程调用函数时传入参数

当 调用pthread_create  第三个和第四个参数不为空时,要注意第四个参数的传递方法

一段流行的代码:

#include 
#include 

using namespace std;

#define NUM_THREADS 10

void* say_hello(void* args)
{
    int i = *((int*)args);//对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取;
    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);//传入的时候必须强制转换为void* 类型,即无类型指针
        cout << "Current pthread id =" << tids[i] << endl;//这里学会使用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
7
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 
#include 

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   
#include  
  
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


今天复习并行与分布式计算复习到这,感到很迷惑,为什么i的值会出错,然后又没在课件和百度里找到答案,就瞎逼分析一下……如果有误,请不吝指教……

update 2017年11月8日:

这篇文章写于十个月前…… 今天再看一下发现当时的理解有点错误,阅读量还有1700+,很惭愧,已经改过来了。




你可能感兴趣的:(Linux多线程编程 —— 线程调用函数时传入参数)