【问题标题】:Get stuck executing multithreads (pthread on Windows)卡住执行多线程(Windows 上的 pthread)
【发布时间】:2017-11-14 02:01:03
【问题描述】:

我希望你能帮助我解决我的麻烦。我的程序正在做一些我不太了解的事情。 程序目的如下: 创建两个线程(task_timer 和 task_read)。 这些线程必须在标准输出 (stdout) 上显示以下消息: “第 1 个 第 2 节 第一节 第 2 节 ……”

代码:

static void* task_timer(void* arg);
static void* task_read(void* p);
struct strShared{
    pthread_mutex_t mut;
    pthread_mutex_t mut2;
    pthread_cond_t synchro;
    pthread_cond_t synchro2;
};


struct strTimer{
    int wt;
    strShared* psh;
};

static void* task_timer(void* p){
    time_t echeance;
    strTimer* timer;
    int time_waiting = 1000;
    time_t  now;
    if(p != NULL){
        timer = p;
        time_waiting = timer->wt; //ms
        echeance = time (NULL) + TIME_OF_THREAD;

        while (1)
        {
            pthread_mutex_lock(&timer->psh->mut);
            printf("Tache 1\n");
            pthread_cond_signal(&timer->psh->synchro);
            pthread_cond_wait(&timer->psh->synchro2, &timer->psh->mut);
            pthread_mutex_unlock(&timer->psh->mut);
        }
    }
    return NULL;
}

static void* task_read(void* p){
    strTimer* timer;
    if(p != NULL){
        timer = p;
        while(1){
            pthread_mutex_lock(&timer->psh->mut);
            pthread_cond_wait(&timer->psh->synchro, &timer->psh->mut);
            printf("Tache 2\n");
            pthread_cond_signal(&timer->psh->synchro2);
            pthread_mutex_unlock(&timer->psh->mut);
        }

    }
    return NULL;
}
int main (void)
{

    pthread_t ttimer;
    pthread_t tread;

    /* TIMER */
    strTimer timer;
    strShared shtimer;
    shtimer.mut = PTHREAD_MUTEX_INITIALIZER;
    shtimer.mut2 = PTHREAD_MUTEX_INITIALIZER;
    shtimer.synchro = PTHREAD_COND_INITIALIZER;
    shtimer.synchro2 = PTHREAD_COND_INITIALIZER;
    timer.psh = &shtimer;
    timer.wt = 1000;

    /* Threads */
    pthread_create(&ttimer, NULL, task_timer, &timer);
    pthread_create(&tread, NULL, task_read, &timer);

   pthread_join(ttimer,NULL);
   pthread_join(tread,NULL);
   return 0;
}

在我看来,这段代码是实现这一目标的好方法。但是,它不起作用,然后我想我犯了一些错误。据我说,它的工作方式如下:

  1. 两个线程都是并行创建和执行的
  2. Task_read 获取 mutex mut,等待信号同步并释放 mutex,因为信号永远不会到达
  3. Task_timer 获取 mutex mut 并在标准输出上显示“Tache 1”
  4. 然后,Task_timer 发送信号 synchro 并等待信号 synchro2(释放互斥锁,因为信号永远不会到达)
  5. Task_read 接收到同步信号并获取 mutex mut 并显示“Tache 2”
  6. Task_read 发送信号 synchro2 并释放 mutex mut 并转到 While 循环的开头
  7. Task_timer 接收到信号 synchro2 并释放互斥体并转到 While 循环的开头

然而,事情并不是这样发生的。实际上,似乎程序在显示“Tache 1”后卡住了。有人可以解释一下为什么会这样吗?我想我觉得不好,但我想理解......

非常感谢, esc39

【问题讨论】:

  • 首先:为什么你认为task_read 会首先获得互斥锁?如果两个线程争夺锁,您无法预测哪一个会被授予锁。从程序员的角度来看,您可以将其视为随机的。所以,请分析一下task_timer先获取锁会发生什么。
  • 您好,感谢您的回答。是的,我假设了这种情况,但我知道还有另一种情况。无论如何,pthread_cond_wait 都会强制 task_read 等待信号。
  • 不,不会。请编辑您的问题并分析案例task_timer先获取锁。
  • 提示:信号没有排队。如果在没有人等待的情况下发送信号,它就会丢失。

标签: c multithreading synchronization pthreads


【解决方案1】:

如果您不熟悉多线程,我建议不要使用条件变量。您不需要为您描述的目标使用任何条件变量。所以删除两个线程中的 pthread_cond_wait 和 pthread_cond_signal 行。

相反,您可以在每个线程中解锁互斥锁后简单地添加睡眠功能。例如task_read 可以修改为:

pthread_mutex_lock(&timer->psh->mut);    
printf("Tache 2\n");
pthread_mutex_unlock(&timer->psh->mut);
usleep(10000);

【讨论】:

    【解决方案2】:

    让我们看看task_timer先获取锁会发生什么。

    pthread_mutex_lock(&timer->psh->mut);      /*acquires a lock*/                                     /*|*/
    printf("Tache 1\n");                       /*output*/                                              /*|*/
    pthread_cond_signal(&timer->psh->synchro); /*sends a signal*/                                      /*|*/ /* no one is waiting for the signal on this cond, so the signal is ignored*/
    pthread_cond_wait(&timer->psh->synchro2, &timer->psh->mut); /*releases lock and waits for signal*/ /*|*/ pthread_mutex_lock(&timer->psh->mut); /*acquires a lock*/
                                                                                                       /*|*/ pthread_cond_wait(&timer->psh->synchro, &timer->psh->mut); /*releases lock and waits for signal*/
                                                                                                       /*|*/
                                                                                                       /*|*/
                                                                                                       /*|*/
                                                                                                       /*|*/ printf("Tache 2\n"); /*never happens*/
    pthread_mutex_unlock(&timer->psh->mut);    /*never happens*/                                       /*|*/ pthread_cond_signal(&timer->psh->synchro2);
                                                                                                       /*|*/ pthread_mutex_unlock(&timer->psh->mut);
    

    死锁。

    简单方法:将您的 pthread_cond_signal() 调用放在临界区之外。将其视为经验法则。当您有几个或多个线程与同一组 mutex/cond 同步时,我几乎无法想象从临界区发出信号是合理的场景。信号和广播的语义就像:嘿,伙计们,我已经完成了我在关键资源方面的工作,你们可以继续。当线程在临界区内时,通过发出cond 信号,它会做出错误的声明。因为它没有完成。

    顺便说一句,在您的情况下,您需要一个额外的标志来告诉应该运行哪个线程。并且仅在标志表明轮到另一个线程时才调用pthread_cond_wait()

    因此,每个线程的基本算法将是(在伪代码中):

    while(loop_again) {
      do_processing_on_non_critical_resources(); /* it's optional */
      lock(mutex);
      while(not_my_turn) { /* explained later */
        wait(cond,mutex);
      }
      do_processsing_on_critical_resources();
      set_flag_to_other_thread();
      unlock(mutex);
      signal(cond);
      do_processing_on_non_critical_resources(); /* it's optional */
    }
    

    not_my_turn 的检查是在 while 循环中而不是简单的 if 检查,因为根据文档,可能存在虚假唤醒 pthread_cond_timedwait()pthread_cond_wait():

    当使用条件变量时,总是有一个布尔谓词涉及与每个条件相关的共享变量等待如果线程应该继续,则该条件等待为真。可能会发生来自pthread_cond_timedwait()pthread_cond_wait() 函数的虚假唤醒。由于来自pthread_cond_timedwait()pthread_cond_wait() 的返回并不暗示此谓词的任何值,因此应根据此类返回重新评估谓词。

    所以,上面你有一个同步线程的一般情况。但是,对于您的情况,answer from M.KHd 是正确且足够的。

    【讨论】:

      猜你喜欢
      • 2021-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-26
      • 1970-01-01
      相关资源
      最近更新 更多