2015-02-17 113 views
4

我从高级Linux编程书中获得了此代码。当我尝试在Linux 64位环境下执行代码时,函数调用后which_prime变量的值被破坏(更改为0)。pthread_join损坏堆栈中的pthread_create参数

在这个例子中为什么which_prime值运行在pthread_join后,被破坏?

一般我们可以使用传递给pthread_create的安全函数内部主即使我们所说的其他功能,如pthread_join()的第四个参数?

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

/* Compute successive prime numbers (very inefficiently). Return the 
Nth prime number, where N is the value pointed to by *ARG. */ 
void* compute_prime (void* arg) 
{ 
    int candidate = 2; 

    int n = *((int*) arg); 
    while (1) { 
     int factor; 
     int is_prime = 1; 
     /* Test primality by successive division. */ 
     for (factor = 2; factor < candidate; ++factor) 
      if (candidate % factor == 0) { 
       is_prime = 0; 
       break; 
      } 
     /* Is this the prime number we’re looking for? */ 
     if (is_prime) { 
      if (--n == 0) 
       /* Return the desired prime number as the thread return value. */ 
       return (void*) candidate; 
     } 
     ++candidate; 
    } 
    return NULL; 
} 

int main() 
{ 
    pthread_t thread; 
    int which_prime = 5000; 
    int prime; 
    /* Start the computing thread, up to the 5,000th prime number. */ 
    pthread_create (&thread, NULL, &compute_prime, &which_prime); 
    /* Do some other work here... */ 
    /* Wait for the prime number thread to complete, and get the result. */ 
    pthread_join (thread, (void*) &prime); 
    /* Print the largest prime it computed. */ 
    printf(“The %dth prime number is %d.\n”, which_prime, prime); 
    return 0; 
} 
+0

这里有一个提示:原型为'pthread_join'是'INT在pthread_join(的pthread_t线程,无效** RETVAL);' - 在那第二个参数密切关注。 – 2015-02-18 03:56:16

回答

4

我们已经在某个时间点不再安全的intpointer之间进行转换到来。这是因为有64位系统的指针是64位的,但int只有32位。

因此,假设32位int和64位指针,这里发生的事情在你的代码。 pthread_join的第二个参数是一个指针指针。换句话说,你应该传递一个指针的地址(一个64位值的地址)。相反,您传递的地址是prime(32位值的地址)。当pthread_join写入结果时,它将覆盖which_prime,因为which_prime在内存中跟随prime

为了解决这个问题,你需要避免int和指针之间的转换。一种方法是避免使用pthread_join的第二个参数,如以下代码所示。

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

#define NUM_THREADS 20 

typedef struct 
{ 
    int success; 
    int input; 
    int output; 
} stData; 

void *doSomething(void *arg) 
{ 
    stData *dataptr = arg; 

    dataptr->success = 1; 
    dataptr->output = dataptr->input * 2; 
    return NULL; 
} 

int main(void) 
{ 
    int i; 
    pthread_t id[NUM_THREADS]; 
    stData data[NUM_THREADS] = {{0}}; 

    for (i = 0; i < NUM_THREADS; i++) 
    { 
     data[i].input = i + 1; 
     pthread_create(&id[i], NULL, doSomething, &data[i]); 
    } 

    for (i = 0; i < NUM_THREADS; i++) 
    { 
     pthread_join(id[i], NULL); 

     if (data[i].success) 
      printf("thread %2d: input=%2d output=%2d\n", i+1, data[i].input, data[i].output); 
     else 
      printf("thread %2d: failed\n", i+1); 
    } 

    return 0; 
} 
+1

或者使用一个专门为每个标准设计的整数类型来保存一个数据指针:'intptr_t'。 – WhozCraig 2015-02-18 00:11:40

+1

@WhozCraig通常,我认为最好尽可能避免在指针和整数类型之间进行转换。这是可以避免铸造的情况之一。 – user3386109 2015-02-18 00:22:07

+0

如果我们有多个子线程会发生什么?我们如何捕获每个线程的结果?我们可以使用互斥体来保护相同的数据结构,并让每个线程更新数据结构的一部分,但是还有其他(更好的)方法吗? – 2015-02-18 03:34:28