0

下面的代码是一个简单的线程游戏,它在线程之间切换导致计时器减少。

它适用于 3 个线程,导致 4 个线程的原因和中止(核心转储),并导致 5 个或更多线程的段错误。

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

#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;
}
4

1 回答 1

2

请参阅下文,了解为什么不应该依赖volatile于 pthreads。但是,您的具体问题可能是因为您在实际设置from之前根据num_of_threads变量malloc 了 pthread 数组:num_of_threadargv[]

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;

并且,在 main 中,在启动任何其他线程之前:

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);

哦,是的,虽然我没有这样做,但您还应该检查所有这些互斥调用的返回码。我不打算添加它,因为它会用不必要的细节堵塞答案,但这是一个很好的做法。

于 2010-03-12T03:49:04.520 回答