【问题标题】:pthread_exit and/or pthread_join causing Abort and SegFaultspthread_exit 和/或 pthread_join 导致 Abort 和 SegFaults
【发布时间】:2010-03-12 03:45:01
【问题描述】:

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

它适用于 3 个线程,导致 4 个线程的原因和 Abort(core dumped),并导致 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;
}

【问题讨论】:

    标签: c multithreading pthreads volatile


    【解决方案1】:

    请参阅下文,了解为什么您不应该在 pthreads 中依赖 volatile。但是,您的特定问题可能是因为您根据num_of_threads 变量之前分配了您的pthread 数组,而您实际上已经从argv[] 设置了num_of_thread

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

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

    【讨论】:

    • 是的,我已经读过了。但是,这是针对特定学校作业的。我已经仔细地 printf 调试了这个问题,它只围绕 pthread_exit/join 调用......所以我不认为这是一个内存问题。
    • 他在答案的第一段中非常清楚地说明了您的问题...再读一遍。
    • @Soapbox,平心而论,我声明现在。我最初的回答是反对 volatile,虽然这是很好的建议,但并不是问题的具体原因。
    • 对不起,我回复了帖子的早期版本。谢谢。我只是在这种愚蠢上浪费了我的下午。
    猜你喜欢
    • 2012-01-20
    • 2015-01-29
    • 1970-01-01
    • 2021-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-06
    • 1970-01-01
    相关资源
    最近更新 更多