2015-02-17 158 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