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

时间:2019-06-21 16:59:13

标签: c multithreading pthreads

TL; DR

我有一个家庭作业,需要实现线程同步。在实现过程中,我担心pthread_cond_wait()是否还会在伪启动时(而不只是在成功唤醒时)锁定互斥体。

任务

任务是一个工作人员/交货问题,其中存在为工作人员提供订单的工作人员线程和管理线程。为此,有一个订单清单,工人可以从中获取订单,管理线程可以下达订单。订单列表是共享对象,需要同步。

到目前为止我的解决方法

我认为,对于这个问题,我需要一个列表监控器,以保护列表不被清空或在其满时不被存放。

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和两个条件变量以向另一个线程发出信号。因此,如果工作人员从列表中删除任务,它将向管理线程发出“不为空”的信号,以便它可以存入其他订单。如果管理者向工作线程发出订单指示“不是空的”,那么一个线程可以从列表中删除订单。

我的担忧

到目前为止,还不错,但现在我认为,有一个事件可能导致上述解决方案很关键,这是“虚假唤醒”。从this thread中,我了解到,如果相应的互斥体mut_order_access被另一个线程锁定,则该线程无法虚假地唤醒。但是,例如,如果列表中仅存在一个顺序,使得get_count(ol) >= MAX_ORDERS不满足,并且线程从等待中虚假地唤醒,检查条件并将其标记为不正确,并跳出循环,该怎么办?然后,另一个线程接收到该信号,并在前一个线程已进入关键区域后正常唤醒,从而锁定互斥锁。因此,现在两个线程都将处于关键区域,这将是一个失败。

问题

因此,仅当线程在虚假唤醒时未锁定互斥锁时,才会发生上述情况,那么是否在虚假唤醒时锁定互斥锁?

2 个答案:

答案 0 :(得分:3)

一个可能返回互斥锁已锁定或未锁定的函数,使您无法分辨出两者之间的区别是完全无法使用的。您怎么可能知道是否要解锁互斥锁?

pthread_cond_wait函数提供了原子的“解锁并等待”操作。它总是在返回之前重新获取互斥量。当然,假设您没有违反任何先决条件。

您可以将其视为三个步骤:

  1. 原子解锁并等待。
  2. 另一个线程发出信号/广播状态或出现虚假唤醒。
  3. 重新获取互斥锁

只有完成所有三个步骤后,pthread_cond_wait才会返回。当等待结束而没有其他线程发送信号时,将发生虚假唤醒。

答案 1 :(得分:3)

您对虚假唤醒条件的理解似乎不正确。伪唤醒与互斥锁是否被另一个线程锁定无关。除非您遇到编程错误(破坏这些函数的约定,一般的内存损坏或其他未定义的行为等),否则pthread_cond_wait永远不会在没有被调用线程持有(锁定)互斥锁的情况下返回。万一发生虚假唤醒,在重新获取互斥锁之前,它仍然无法返回。即使在使用pthread_cancel取消了服务员的情况下,取消清理处理程序也无法开始运行,直到重新获取互斥体为止。