【问题标题】:pthreads_cond_broadcast doesn't wake up all threads that are waitingpthreads_cond_broadcast 不会唤醒所有正在等待的线程
【发布时间】:2021-01-05 17:33:12
【问题描述】:

我正在创建一个多线程文件服务器。我创建了一个线程池来处理来自客户端的请求。这是我的线程池代码:

void *worker_call(void *thread_id){
    printf("Initialized thread #%ld \n", (long)thread_id);  

    //pull in initialized global configs
    extern pthread_mutex_t queue_m;
    extern pthread_cond_t worker_c;
    extern steque_t queue; 


    while(1){
        if (pthread_mutex_lock(&queue_m) != 0){
            fprintf(stderr, "An error occured while locking mutex in #%ld \n", (long)thread_id);
        }
            while(steque_isempty(&queue) == 1){
                printf("thread #%ld - Going to sleep...\n", (long) thread_id);
                pthread_cond_wait(&worker_c, &queue_m);
                printf("thread #%ld - I'm waking up...\n", (long) thread_id);
            }
            
            int *work = steque_pop(&queue);
            

        if (pthread_mutex_unlock(&queue_m) != 0){
            fprintf(stderr, "An error occured while unlocking mutex in #%ld \n", (long)thread_id);
        }

        pthread_cond_broadcast(&worker_c);

        sleep(1); //added to make sure that the other threads have a chance to wake up

        printf("thread #%ld - what is the value of work: %d\n", (long) thread_id, *work);
        // process_request(&(work->ctx), work->path, work->arg, (long)thread_id);
        free(work);
    }

    return NULL;
}

为了测试我的线程池是否正确启动并且所有线程都在工作,我创建了以下测试:

int main(){
    int nthreads = 6;

    pthread_t threads[nthreads];
    long thread_ids[nthreads];

    pthread_attr_t thread_attr;
    pthread_attr_init(&thread_attr);
    pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
    pthread_attr_setscope(&thread_attr, PTHREAD_SCOPE_SYSTEM);


    for(int t=0;t<nthreads;t++){
        thread_ids[t] = t;
        if( pthread_create(&threads[t],NULL, worker_call, (void *)thread_ids[t]) != 0 ) {
            printf("An error occured while creating thread: %d\n", t);
        }
    }

    pthread_attr_destroy(&thread_attr);

    //init multi-threading configs
    extern pthread_mutex_t queue_m;

    pthread_mutexattr_t m_attr;
    pthread_mutexattr_init(&m_attr);
    pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_ERRORCHECK);

    pthread_mutex_init(&queue_m, NULL);

    extern pthread_cond_t worker_c;
    pthread_cond_init(&worker_c, NULL);

    extern steque_t queue; 

    steque_init(&queue);
    //create a simple queue with each item being an int. 
    //the goal is to simply remove the items from the queue.
    for(int i = 0; i < 5; i++){
        
        int *work = malloc(sizeof(int));
        *work = i;
        steque_enqueue(&queue, work);
    }
    

    printf("queue size after creating it: %d\n", steque_size(&queue));

    pthread_cond_broadcast(&worker_c);

    for(int t=0;t<nthreads;t++){
        pthread_join(threads[t], NULL);
    }

    printf("All the threads finished processing\n");

    return 0;
}

现在这段代码成功运行,但是,只有一个线程被唤醒,它完成了所有工作,如下面的输出所示:

Initialized thread #0 
thread #0 - Going to sleep...
Initialized thread #1 
thread #1 - Going to sleep...
Initialized thread #2 
thread #2 - Going to sleep...
Initialized thread #3 
thread #3 - Going to sleep...
Initialized thread #4 
thread #4 - Going to sleep...
Initialized thread #5 
thread #5 - Going to sleep...
queue size after creating it: 5
thread #5 - I'm waking up...
thread #5 - what is the value of work: 0
thread #5 - what is the value of work: 1
thread #5 - what is the value of work: 2
thread #5 - what is the value of work: 3
thread #5 - what is the value of work: 4
thread #5 - Going to sleep...

我的问题是,为什么其他线程没有醒来并从队列中抓取项目?我尝试在发出 pthread_cond_broadcast 后添加一秒钟的延迟,以给其他线程足够的时间来锁定互斥锁,但我没有成功使用该方法。有人看到我做错了吗?

【问题讨论】:

  • 在解锁保护它的互斥锁后,您似乎正在访问共享状态。这可能不是主要错误,但至少是其中一个错误。
  • 您具体在哪里看到该错误?
  • 请分享worker_call代码。
  • @user58967 worker_call 代码是上面的第一块代码。
  • 我认为我错了 - 我误解了 work 指向共享数据。

标签: c multithreading posix


【解决方案1】:

我无法发表简单的评论,因为我没有 +50 代表。所以我必须发布这个作为答案。

当您说“成功运行”时,您对成功的定义是什么?这里有几个令人担忧的比赛条件,我很惊讶这完全有效。例如,worker_call() 是在实际初始化之前访问队列、互斥锁和 cond 变量。

此外,当您最初在 main() 中将项目推入队列时,您应该将该代码包装在互斥锁中,因为您的线程也试图访问该变量。

我的建议是您修复这些竞争条件,然后更新您的结果。

编辑 1:

此外,在 worker_call 中,worker 在调用 cond_wait 之前会检查队列是否为空,但它不会在唤醒后检查是否为空。由于项目的数量可能少于工作人员,因此该函数将需要再次检查队列是否为空。

【讨论】:

  • 哇,就是这样,我在初始化之前使用了互斥锁和 cond 变量。感谢您的帮助!通过“成功运行”,我的意思是,当我运行它来传输文件时——它起作用了。传输的文件。但并非总是如此。偶尔它只是坏了。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-31
  • 2013-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-09
相关资源
最近更新 更多