2010-03-12 101 views
0

以下代码是一个简单的线程游戏,它在线程间切换,导致计时器减少。pthread_exit和/或pthread_join导致Abort和SegFaults

它适用于3个线程,原因和中止(核心转储)为4个线程,并导致5个或更多线程seg故障。

任何人都知道为什么会发生这种情况?

#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 
#include <errno.h> 
#include <assert.h> 

int volatile num_of_threads; 
int volatile time_per_round; 
int volatile time_left; 
int volatile turn_id; 
int volatile thread_running; 
int volatile can_check; 
void * player (void * id_in){ 
int id= (int)id_in; 
while(1){ 
    if(can_check){ 
    if (time_left<=0){ 
    break; 
    } 
    can_check=0; 
    if(thread_running){ 
    if(turn_id==id-1){ 
    turn_id=random()%num_of_threads; 
    time_left--; 
    } 
    } 
    can_check=1; 
    } 
} 
pthread_exit(NULL); 
} 
int main(int argc, char *args[]){ 
int i; 
int buffer; 
pthread_t * threads =(pthread_t *)malloc(num_of_threads*sizeof(pthread_t)); 
thread_running=0; 
num_of_threads=atoi(args[1]); 
can_check=0; 
time_per_round = atoi(args[2]); 
time_left=time_per_round; 
srandom(time(NULL)); 
//Create Threads 
for (i=0;i<num_of_threads;i++){ 
    do{ 
    buffer=pthread_create(&threads[i],NULL,player,(void *)(i+1)); 
    }while(buffer == EAGAIN); 
} 
can_check=1; 

time_left=time_per_round; 
turn_id=random()%num_of_threads; 
thread_running=1; 

for (i=0;i<num_of_threads;i++){ 
    assert(!pthread_join(threads[i], NULL)); 
} 
return 0; 
} 

回答

2

下面查看为什么你不应该在并行线程依赖于volatile。然而,你具体问题,可能是因为你,你实际上已经设置num_of_threadargv[]之前的malloc您的并行线程阵列的基础上,num_of_threads变量

pthread_t *threads = (pthread_t *)malloc (num_of_threads * sizeof (pthread_t)); 
thread_running = 0; 
num_of_threads = atoi (args[1]); 

所以这是一个非常好的机会你”重新编写threads阵列。在启动时,num_of_threads变量可能为零,这意味着您没有分配您认为的自己。提取参数后将分配移动到,并且应该修复它。


而现在,为您的观赏乐趣:-),我对不安全使用的volatile,我仍然站在原来的咆哮。

不是依靠volatile来保护您的共享变量。正确的方法是使用pthread_mutex_blab_blah_blah调用。

特别值得注意的是,检查此代码段:

if (can_check) { 
    if (time_left <= 0) { 
    break; 
    } 
    // URK!! 
    can_check=0; 

URK!!是你当前线程可以切换出来,另一个运行点,导致可能性线程可以运行的关键部分代码。

我的建议是完全忘记了can_check,只是保护所有的共享变量与互斥,类似(从内存中):

void *player (void * id_in) { 
    int id = (int)id_in; 
    while (1) { 
     pthread_mutex_lock (&mutex); 
     if (time_left <= 0) { 
      pthread_mutex_unlock (&mutex); 
      break; 
     } 
     if (thread_running) { 
      if (turn_id == id-1) { 
       turn_id = random() % num_of_threads; 
       time_left--; 
      } 
     } 
     pthread_mutex_unlock (&mutex); 
    } 
    pthread_exit(NULL); 
} 

然后在文件级放:

pthread_mutexattr_t mutexattr; // prob. not needed at file level. 
pthread_mutex_t mutex; 

,主要是在启动任何其他线程之前:

pthread_mutexattr_init (&mutexattr); 
// Change attributes if needed. 
pthread_mutex_init (&mutex, &mutex_attr); 

// Then run all you other stuff here, make sure you've joined with all threads. 

pthread_mutex_destroy (&mutex); 

哦是啊,altho呃我没有做到这一点,你还应该检查所有这些互斥调用的返回码。我不会补充说,因为它会阻塞不必要的细节,但这是一个很好的做法。

+0

是的,我读过这个。但是,这是针对特定的学校作业。 我仔细printf调试了这个问题,它只围绕着pthread_exit/join调用...所以我不认为它是一个内存问题。 – mjames 2010-03-12 03:57:40

+0

Protip:printf在多线程环境中告诉你什么都没有。 – 2010-03-12 04:14:21

+0

他非常清楚地说明你的问题在答案的第一段中是什么......再次阅读。 – SoapBox 2010-03-12 04:14:55