pthread_cond_wait 是否锁定互斥量和虚假唤醒

Does pthread_cond_wait lock the mutex und spurious wakeup

TL;DR

我有一个作业需要实现线程同步。在实现过程中,我担心 pthread_cond_wait() 是否也锁定互斥量,如果它虚假地唤醒,而不仅仅是它成功唤醒。

任务

任务是一个 worker/delivery 问题,其中有工作线程和管理线程为工作线程提供命令。为此,有一个订单列表,工作人员从中获取订单,管理线程存放订单。订单列表是共享对象,需要同步

到目前为止我的解决方案

我想到了这个问题,我需要一个列表监视器来保护它免受空时访问或当它已满时的存款。

void deposit_order(order_list* ol, order* o){
    pthread_mutex_lock(&(ol->mut_order_access));
    while(get_count(ol) >= MAX_ORDERS) {
        pthread_cond_wait(&(ol->cond_not_full), &(ol->mut_order_access));
    }

    ol->orders[ol->head] = o;
    ol->head = (ol->head+1)%MAX_ORDERS;
    ol->count++;
    
    pthread_cond_signal(&(ol->cond_not_empty));
    pthread_mutex_unlock(&(ol->mut_order_access));
}

order* get_order(order_list* ol) {
    pthread_mutex_lock(&(ol->mut_order_access));
    while(get_count(ol) <= 0) {
        pthread_cond_wait(&(ol->cond_not_empty), &(ol->mut_order_access));
    }
    order* o;
    
    o = ol->orders[ol->tail];
    ol->tail = (ol->tail+1)%MAX_ORDERS;
    ol->count--;
    
    pthread_cond_signal(&(ol->cond_not_full));
    pthread_mutex_unlock(&(ol->mut_order_access));
    return o;
}

我认为结构包含什么并不重要,因为主题是如何同步访问。显然结构 order_list 包含一个 mutex 和两个条件变量来通知另一个线程。因此,如果一个工作人员从列表中删除一个任务,它会向管理线程发出“不为空”的信号,以便它可以存放一个额外的订单。如果管理层存入一个订单,则向工作线程发出“非空”信号,这样一个线程就可以从列表中删除一个订单。

我的担忧

到目前为止一切顺利,但现在我认为上述解决方案可能很关键,这就是“虚假唤醒”。从 我了解到,如果相应的互斥锁 mut_order_access 被另一个线程锁定,则线程不能虚假唤醒。但是,如果列表中只有一个订单,以至于 get_count(ol) >= MAX_ORDERS 没有被满足,并且一个线程虚假地从等待中醒来,检查条件并将其标记为不正确并跳出循环怎么办。然后另一个线程收到信号并在前一个线程已经在临界区后正常唤醒并锁定互斥锁。所以现在两个线程都在关键区域,这将是失败的。

问题

所以只有当一个线程在虚假唤醒时没有锁定互斥量时才会发生上述情况,那么它是否在虚假唤醒时锁定了互斥量?

一个可能 return 锁定或解锁互斥量并且让您无法分辨其中差异的函数将完全无法使用。你怎么可能知道是否解锁互斥量?

pthread_cond_wait函数提供了原子"unlock and wait"操作。它总是 re-acquires returning 之前的互斥体。当然,假设您没有违反其中的任何一项 pre-conditions。

您可以将其视为一个 three-step 过程:

  1. 原子解锁并等待。
  2. 另一个线程 signals/broadcasts 条件或存在虚假唤醒。
  3. Re-acquire互斥锁

只有当所有三个步骤都完成后,才会 pthread_cond_wait return。当等待结束而没有另一个线程发送信号时,就会发生虚假唤醒。

您对虚假唤醒条件的理解似乎不正确。虚假唤醒与互斥量是否被另一个线程锁定无关。除非您有编程错误(破坏这些函数的契约、一般内存损坏或其他未定义的行为等),否则 pthread_cond_wait 永远不会 return 如果调用线程不持有(锁定)互斥锁.在虚假唤醒的情况下,它仍然无法 return,直到互斥锁为 re-acquired。即使在使用 pthread_cancel 取消服务员的情况下,取消清理处理程序也无法开始 运行,直到互斥锁为 re-acquired。