• park()可以让调用线程休眠
  • unpark(tid)可以唤醒tid标识的线程
  • setpark()可以使线程从park直接返回(其他线程调用unpark后)
  • Linux系统里提供futex实现类似机制

为什么我们需要条件变量

void* child(void* arg){
    printf("child\n");
    // XXX how to indicate we are done?
    return NULL;
}

int main(int argc, char* argv[]){
    printf("parent: begin\n");
    pthread_t c;
    pthread_create(&c, NULL, child, NULL); // create child
    // xxx how to wait for child?
    printf("parent: end\n");
    return 0;
}

一种低效的实现

volatile int done = 0;

void* child(void* arg){
    printf("child\n");
    done = 1;
    return NULL;
}

int main(int argc, char* argv[]){
    printf("parent: begin\n");
    pthread_t c;
    pthread_create(&c, NULL, child, NULL); // create child
    while(done == 0)
        ; // spin
    printf("parent: end\n");
    return 0;
}

目的:判断A是否执行完,A执行完后B才能执行

enter image description here

有什么优化方案?

尝试:引入条件变量

enter image description here

存在问题:线程2执行完第一行后,时间片耗尽,转而先执行线程1的unpark(此时没有休眠线程可以释放),回到线程2后park进入休眠无法释放。

一个简单规则:发送信号时记得加锁

enter image description here

问题:执行线程2的park后,锁没有unlock,陷入死锁

enter image description here

问题:线程2执行完第4行unlock后,可能时间片耗尽跳转到线程1先执行unpark(此时没有休眠线程可以释放),回到线程2后park进入休眠无法释放。

enter image description here

条件变量类型:抽象

enter image description here

enter image description here

事件类型:抽象

enter image description here

enter image description here

  • 条件变量
    • 是一个显示队列,根据某些特定条件(condition)决定线程行为
    • 等待条件:当条件不满足时,线程加入队列(休眠)
    • 发出信号:对于其他线程,当改变状态(使条件满足时),可以在队列中唤醒线程

“条件变量”小结

  • 条件变量可以处理线程的等待和唤醒
  • 在条件变量上等待的线程,会在休眠前解锁,被唤醒后立刻加锁
  • 通过条件变量可以实现事件类型,描述事件发生与否
  • 如何用条件变量解决实际问题?