多线程模型:生产者和消费者

如果仅使用用pthread_mutex,为了检查条件是否满足,需要重复锁定、检查、解锁,这个过程消耗了大量的CPU时间片。

while(true) {
    pthread_mutex_lock(&mutex);
    if (condition)   break;
    pthread_mutex_unlock(&mutex);
}
do_critical();
pthread_mutex_unlock(&mutex);

当然我们可以在检查变量的时候睡眠几秒钟,但这样一旦在条件满足的时候就无法作出快速的反应。我们真正需要的是这样一种过程:在条件不满足时,线程进入睡眠状态,一旦条件满足,线程就会被唤醒。

pthread_cond_wait 和 pthread_cond_signal / pthread_cond_broadcast 正满足这种情况,使用它们最常见的一种方式就是生产者-消费者模型。

void producer() {
    pthread_mutex_lock(&res_lock);
    pthread_cond_signal(&res_cond); // pthread_cond_broadcast
    Produce(&RES);  // ..(a)
    pthread_mutex_unlock(&res_lock);
}

void consumer()
    pthread_mutex_lock(&res_lock);
    while (RES.count == 0) {
        pthread_cond_wait(&res_cond, &res_lock);
    }
    Consume(&RES);
    pthread_mutex_unlock(&res_lock);
}

producer()中,在调用pthread_cond_singal后consumer()被唤醒,但由于res_lock仍被producer锁定,consumer会等待在这个互斥变量上,直到producer释放它。pthread_cond_singal可以在pthread_mutex_lock和pthread_mutex_unlock之间的任何地方,这是由于res_lock被锁定,任何其他线程都无法访问到RES,当然也包括刚被唤醒的consumer。pthread_cond_singal也可以在pthread_mutex_unlock之后 (注1),producer早就不在操作RES,其他线程可以开始重新竞争。

cosumer()中,在pthread_mutex_lock和pthread_cond_wait之间,以及pthread_cond_wait和pthread_mutex_unlock之间,res_lock都被该线程锁定。前一部分就是检查资源是否可用,当没有资源时就等待在res_cond上,同时释放res_lock。这里释放res_lock的结果可能会使得其他的consumer进入,但毫无疑问,它们也会等待在res_cond上;也可能使得producer进入,产生资源后唤醒等待在res_cond上的consumer ,或者使得等待在res_lock上的线程进入 (注2)。可能有多个consumer被同时唤醒,试图从pthread_cond_wait返回,这个返回包含了对res_lock的锁定,也就是说,只有一个consumer可以锁定res_lock,从pthread_cond_wait返回,其他的consumer有等待在res_lock上。从pthread_cond_wait返回的consumer再次检查资源是否可用,由于它是被producer唤醒的第一个线程,资源可用,这个consumer会进入第二部分。第二部分很简单,就是消耗资源,res_lock在这里是被锁定的。第一个consumer退出之后,可能有第二个consumer获得了res_lock(锁定了它),这个consumer和前者一样,检查资源是否可用。要么有资源,消耗它,再释放res_lock;要么没有资源,继续等待在res_cond上,同时也释放res_lock,要么让其他的consumer空欢喜一场,要么让producer来解救饥饿的consumer。

注1和注2:producer()中调用pthread_cond_signal在pthread_mutex_unlock之前还是之后是有一点区别的,前者所有的consumer和producer会等待在res_lock上,一起开始竞争(但实际上由于具体实现的关系,也可能有先后次序),后者只有刚开始执行的producer和consumer,以及准备从pthread_cond_wait返回的consumer(它们发现刚才资源可用,但还需要再检查资源),它们一起开始竞争,而那些发现资源条件不满足的consumer会等到被pthread_cond_signal之后才获得执行的机会。所以为了公平以及一致(所有的东西都等待在res_lock上),建议pthread_cond_signal放在pthread_mutex_unlock之前。

生产者-消费者模型可以实际应用(实际上它们本身就是一个实际的模型)在桌面版的访病毒软件中,把producer当成一个搜索文件并放置文件路径队列的过程,把consumer作为实际扫描病毒的过程,consumer之间读取文件到内存的操作和扫描文件内容(在内存中)的操作可以有效地使用计算机资源。 

你可能感兴趣的:(Programes,编程技巧,C/C++,多线程,signal)