【问题标题】:Conditional wait with pthreads使用 pthread 进行条件等待
【发布时间】:2011-04-04 12:54:54
【问题描述】:

我似乎在使用 pthreads 条件变量时遇到了可能的死锁。

这里是代码

thread function(){
    for (condition){
        do work
        /* should the thread continue? */
        if (exit == 1){
            break; /* exit for */
        } 
     } /* end for */

pthread_mutex_lock(&mtxExit);
exit = 0;
pthread_cond_signal(&condVar);
pthread_mutex_unlock(&mtxExit);
}

主要功能如下:

function main(){
    if (thread is still active){
          pthread_mutex_lock(&mtxExit);
          exit = 1;
          pthread_mutex_unlock(&mtxExit);
          } /* end if */
    while (exit == 1){
       pthread_mutex_lock(&mtxExit);
       /* check again */
       if (exit == 1)
           pthread_cond_wait(&condVar, &mtxExit);
       pthread_mutex_unlock(&mtxExit);
       }
    create new thread()
    ....
    }

代码总是卡在 cond_wait 上。 :(

编辑:

让我在线程中添加一些说明以解释我在做什么。

在任何给定时间,我只需要运行一个线程。我有一个启动线程的函数,告诉它要做什么,主线程继续它的工作。

下一次主线程决定它需要生成另一个线程时,它必须确保之前启动的线程已经退出。我不能同时让两个线程处于活动状态,因为它们会相互干扰。这是设计和我正在处理的问题的定义。

这就是我遇到问题的地方。

这是我的方法:

启动线程,让它完成它的工作。

线程检查其工作的每一步以查看它是否仍然相关。这就是“退出”出现的地方。主线程将“exit”设置为 1,如果它需要告诉线程它不再相关。

在大多数情况下,线程将在主线程决定生成另一个线程之前退出。但是我仍然需要考虑到主线程准备好启动另一个线程时线程仍然处于活动状态的情况。

所以主线程设置了“exit”的值,需要等待线程退出。我不想将 pthread_kill 与 0 一起用作信号,因为这样主线程将处于循环中浪费 CPU 周期。我需要主线程放弃控制并休眠/等待线程退出。

由于我一次只需要一个线程,我不需要担心扩展到更多线程。解决方案永远不会有多个线程。我只需要一个可靠的机制来测试我的线程是否还活着,如果是,则发出信号退出,等待它退出并启动下一个。

从我的测试来看,即使线程可能已经退出或者信号根本没有传递到主线程,主线程仍在输入条件变量。它永远在那里等待。在某些情况下,在调试器中我看到 exit 的值设置为 0 并且主线程仍在等待信号。某些地方似乎存在竞争条件。

我不喜欢我现在如何设置代码,它太乱了。它现在只是一个概念证明,我将很快转向更好的解决方案。我的挑战是可靠地通知线程退出,等待它退出。

感谢您的宝贵时间。

【问题讨论】:

  • 你是在多处理器(注意:不一定是多核)机器上吗?
  • 另外,exit 是如何声明的?
  • exit 是一个全局变量。我在多核、多线程、多 CPU 系统上运行。是的,这三个都成立。
  • 对您的回答来说不是一个问题,但是如果您的要求是完全顺序的行为,为什么还要使用线程?
  • 不,它不是顺序行为。有两个并行的控制流。

标签: c pthreads race-condition


【解决方案1】:

你忘记初始化你的条件变量了吗?

pthread_cond_init(&condVar, NULL)

【讨论】:

  • 我使用了静态初始化 PTHREAD_COND_INITIALIZER
  • 我的条件变量也被声明为全局变量,因为我一次没有多个线程处于活动状态。
【解决方案2】:

while (exit == 1) {

在您引用的代码中,您引用的方式我没有看到任何特别的问题。它不干净,但看起来很实用。是什么让我相信你在其他地方将exit 设置为 0 而没有发出信号。或者线程卡在某处工作。

但是考虑到提示您尝试在启动另一个线程之前发出信号终止一个线程的 cmets,我认为您做错了。通常,如果不能错过信号,则不应依赖 pthread 条件信号。虽然状态变量exit 似乎涵盖了这一点,但它仍然是 IMO 错误应用 pthread 条件。

在这种情况下,您可以尝试使用信号量。在终止时,线程增加终止信号量,以便 main 可以等待(减少)信号量。

thread function()
{
   for (condition)
   {
      do work
      /* should the thread continue? */
      if (exit == 1) {
         break; /* exit for */
      } 
   } /* end for */
   sem_post(&termSema);
}

function main()
{
    if (thread is still active)
    {
          exit = 1;
          sem_wait(&termSema);
          exit = 0;
    }
    create new thread()
    ....
}

一般来说,我可以建议寻找一些线程池实现。因为使用状态变量来同步线程仍然是错误的,并且不能扩展到多个线程。而且容易出错。

【讨论】:

  • @powerrox:semaphore 建议支持,即使有新评论。如果线程完成,则 sema 已经增加并且 main 不会等待。如果线程仍在运行,main 会在递减时阻塞 - 等待线程递增信号量。
【解决方案3】:

代码卡在pthread_cond_wait时,是exit1还是0?如果exit1,应该是卡住了。

如果exit0,则很可能是以下两种情况之一:

1) 一些代码将exit 设置为0,但没有发出条件变量信号。

2) 某些线程在 pthread_cond_wait 上阻塞,消耗了一个信号,但没有做任何你需要做的事情。

【讨论】:

    【解决方案4】:

    您当前的实现存在各种时间问题(因此存在问题)。

    为确保线程已完成(并且其资源已被释放),您应该调用pthread_join()

    这里不需要pthread_cond_t

    使用pthread_cancel() 通知线程不再需要它也可能更有意义,而不是像您当前所做的那样使用标志。

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    void *thread_func(void *arg) {
        int i;
    
        for (i = 0; i < 10; i++) {
            /* protect any regions that must not be cancelled... */
            pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    
            /* very important work */
            printf("%d\n", i);
    
            pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    
            /* force a check to see if we're finished */
            pthread_testcancel();
    
            /* sleep (for clarity in the example) */
            sleep(1);
        }
    
        return NULL;
    }
    
    void main(void) {
        int ret;
        pthread_t tid;
    
        ret = pthread_create(&tid, NULL, thread_func, NULL);
        if (ret != 0) {
            printf("pthread_create() failed %d\n", ret);
            exit(1);
        }
    
        sleep(5);
    
        ret = pthread_cancel(tid);
        if (ret != 0) {
            printf("pthread_cancel() failed %d\n", ret);
            exit(1);
        }
    
        ret = pthread_join(tid, NULL);
        if (ret != 0) {
            printf("pthread_join() failed %d\n", ret);
            exit(1);
        }
    
        printf("finished...\n");
    }
    

    还有一点值得注意:

    • exit() 是一个库函数 - 你不应该声明任何与其他东西同名的东西。
    • 根据您的具体情况,让单个线程始终保持活动状态并为其提供作业而不是连续创建/取消线程可能是有意义的(研究“线程池”)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-07
      • 1970-01-01
      • 2016-06-21
      • 2017-10-17
      • 1970-01-01
      • 2013-12-15
      • 1970-01-01
      相关资源
      最近更新 更多