【问题标题】:unexpected result when type casting the return value from pthread in C在 C 中从 pthread 类型转换返回值时出现意外结果
【发布时间】:2017-05-21 03:14:29
【问题描述】:

我在 C 中练习 pthread,尝试制定一个在线程之间尽可能平均分配工作负载的函数。每个线程返回一个整数,表示分配给它们的工作量。

#include <stdio.h>
#include <pthread.h>
#define THREADS 3
#define ITEMS 10

pthread_mutex_t locker = PTHREAD_MUTEX_INITIALIZER;
void* worker(void* arg){
    int id = *(int*)arg;
    int chunk = ITEMS/THREADS;
    int start = chunk * id;
    int end = id == THREADS - 1 ? ITEMS : start + chunk;
    for(int i = start; i < end; i ++){
        //do some work; 
    }
    pthread_mutex_lock(&locker);
    //do some work
    pthread_mutex_unlock(&locker);
    return end - start;  //here return type should be (void *), I casted it to (int).
}

int main(void){
    pthread_t ids[THREADS];
    int args[THREADS];
    for(int i = 0; i < THREADS; i ++){
        args[i] = i;
        pthread_create(ids + i,NULL,worker,&args[i]);
    }

    int total = 0;
    int temp;
    for(int i = 0; i < THREADS; i ++){
        pthread_join(ids[i],&temp); //param here should be (void**), I cast it to (int*)
        total = total +  temp;
        printf("Thread %d process %d items\n",i,temp);
    }

    printf("Get total items:%d\n",total);
}

我想对每个线程的返回值求和,以检查是否所有项目都已处理。因为我比较懒,也只是小练习,所以直接把每个线程的返回值从(void *)投到(int)。然后我得到了一些让我很困惑的输出。事实证明我可以成功地从变量“temp”中读取值,但是当我尝试这样做时

total = total + temp;

total 的值没有改变... 我通过将“temp”的类型从int 更改为long 解决了这个问题,但我不明白为什么内存中的字节数在这里很重要。

这是一个示例输出

Thread 0 process 3 items
Thread 1 process 3 items
Thread 2 process 4 items
Get total items:4

PS:我确实知道这样做的正确方法是什么。 我不明白的是,当我将整数转换为指针然后将其存储在 4 字节内存中时会发生什么? 为什么可以打印出值但无法用它计算? 如果我使用 long(与 void* 大小相同)而不是 int(4 字节内存),那么一切正常的原因是什么?

【问题讨论】:

  • 这一行:int temp; 应该是:int *temp = NULL; 而这一行:total = total + temp; 最好写成:total = total + *temp;
  • 这一行:int chunk = ITEMS/THREADS; 执行整数除法。这种说法有几个问题。一个明显的问题是,如果THREADS 大于ITEMS,那么计算结果将为0。可能不是你想要的。
  • 关于:return end - start; 有更好的方法可以退出线程函数。建议:int result = end-start; pthread_exit( (void*)&amp;result );
  • 调用:pthread_create()时,始终检查返回值以确保操作成功。 IE。 if( 0 != pthread_create(...) ) { perror("pthread_create failed"); }

标签: c types casting pthreads pthread-join


【解决方案1】:

如果你小心谨慎地使用&lt;stdint.h&gt;中的uintptr_t,你可以做你想做的事,像这样:

#include <stdio.h>
#include <pthread.h>
#include <stdint.h>

#define THREADS 3
#define ITEMS 10

pthread_mutex_t locker = PTHREAD_MUTEX_INITIALIZER;

static void *worker(void *arg)
{
    int id = *(int *)arg;
    int chunk = ITEMS / THREADS;
    int start = chunk * id;
    int end = id == THREADS - 1 ? ITEMS : start + chunk;
    for (int i = start; i < end; i++)
        printf("A TID %d: s = %2d; e = %2d; i = %2d\n", id, start, end, i);
    pthread_mutex_lock(&locker);
    for (int i = start; i < end; i++)
        printf("B TID %d: s = %2d; e = %2d; i = %2d\n", id, start, end, i);
    pthread_mutex_unlock(&locker);
    return (void *)(uintptr_t)(end - start);
}

int main(void)
{
    pthread_t ids[THREADS];
    int args[THREADS];
    for (int i = 0; i < THREADS; i++)
    {
        args[i] = i;
        pthread_create(ids + i, NULL, worker, &args[i]);
    }

    int total = 0;
    int temp;
    void *vp;
    for (int i = 0; i < THREADS; i++)
    {
        pthread_join(ids[i], &vp); // param here should be (void**), I cast it to (int*)
        temp = (uintptr_t)vp;
        total = total +  temp;
        printf("Thread %d process %2d items (total = %2d)\n", i, temp, total);
    }

    printf("Get total items: %d\n", total);
    return 0;
}

示例输出:

A TID 0: s =  0; e =  3; i =  0
A TID 1: s =  3; e =  6; i =  3
A TID 2: s =  6; e = 10; i =  6
A TID 0: s =  0; e =  3; i =  1
A TID 1: s =  3; e =  6; i =  4
A TID 2: s =  6; e = 10; i =  7
A TID 0: s =  0; e =  3; i =  2
A TID 1: s =  3; e =  6; i =  5
A TID 2: s =  6; e = 10; i =  8
B TID 0: s =  0; e =  3; i =  0
A TID 2: s =  6; e = 10; i =  9
B TID 0: s =  0; e =  3; i =  1
B TID 0: s =  0; e =  3; i =  2
B TID 1: s =  3; e =  6; i =  3
B TID 1: s =  3; e =  6; i =  4
B TID 1: s =  3; e =  6; i =  5
Thread 0 process  3 items (total =  3)
B TID 2: s =  6; e = 10; i =  6
B TID 2: s =  6; e = 10; i =  7
B TID 2: s =  6; e = 10; i =  8
B TID 2: s =  6; e = 10; i =  9
Thread 1 process  3 items (total =  6)
Thread 2 process  4 items (total = 10)
Get total items: 10

请注意,第一组输出(标记为 A)是交错的。第二组(标记为B)由互斥锁序列化。碰巧它们在此示例输出中按顺序 0、1、2 执行;这是正常的但不是保证的顺序。最后A 行由线程2 生成,而线程0 已锁定互斥锁。父进程在线程 2 仍然忙碌时加入线程 0。

有一次,当输出通过管道传输到日志程序时,我得到:

A TID 0: s =  0; e =  3; i =  0
A TID 2: s =  6; e = 10; i =  6
A TID 1: s =  3; e =  6; i =  3
A TID 0: s =  0; e =  3; i =  1
A TID 2: s =  6; e = 10; i =  7
A TID 1: s =  3; e =  6; i =  4
A TID 0: s =  0; e =  3; i =  2
A TID 2: s =  6; e = 10; i =  8
A TID 1: s =  3; e =  6; i =  5
A TID 2: s =  6; e = 10; i =  9
B TID 0: s =  0; e =  3; i =  0
B TID 0: s =  0; e =  3; i =  1
B TID 0: s =  0; e =  3; i =  2
B TID 1: s =  3; e =  6; i =  3
B TID 1: s =  3; e =  6; i =  4
B TID 1: s =  3; e =  6; i =  5
B TID 2: s =  6; e = 10; i =  6
B TID 2: s =  6; e = 10; i =  7
B TID 2: s =  6; e = 10; i =  8
B TID 2: s =  6; e = 10; i =  9
Thread 0 process  3 items (total =  3)
Thread 1 process  3 items (total =  6)
Thread 2 process  4 items (total = 10)
Get total items: 10

【讨论】:

    【解决方案2】:

    您不能将指针转换为整数类型或从整数类型转换。这样做会调用未定义的行为。您也不能随意将void ** 转换为另一个指针。

    要正确执行此操作,您应该在线程中为返回值动态分配内存,然后返回指向该内存的指针。在主线程中,您需要将void * 的地址传递给pthread_join,然后复制/转换该指针。

    所以你的线程返回它的值是这样的:

    void* worker(void* arg){
        ...
        int *rval = malloc(sizeof(int));
        *rval = end - start;
        return rval;
    }
    

    然后你像这样检索那个值:

    void *vtemp;
    int *temp;
    for(int i = 0; i < THREADS; i ++){
        pthread_join(ids[i],&vtemp);
        temp = vtemp;   // you can cast to/from a void * to another pointer without a cast
        total = total +  *temp;
        printf("Thread %d process %d items\n",i,*temp);
        free(temp);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多