首页 > 解决方案 > 为什么这个使用互斥锁的例子比另一个带有额外条件变量的例子效率低?

问题描述

Linux 编程接口的一个例子:

在生产者线程中,我们将有如下代码:

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static int avail = 0;

/* Code to produce a unit omitted */
s = pthread_mutex_lock(&mtx);
if (s != 0)
    errExitEN(s, "pthread_mutex_lock");

avail++; /* Let consumer know another unit is available */

s = pthread_mutex_unlock(&mtx);
if (s != 0)
    errExitEN(s, "pthread_mutex_unlock");

在主(消费者)线程中,我们可以使用以下代码:

for (;;) {
    s = pthread_mutex_lock(&mtx);
    if (s != 0)
        errExitEN(s, "pthread_mutex_lock");
    while (avail > 0) {
        /* Consume all available units */
        /* Do something with produced unit */
        avail--;
    }
    s = pthread_mutex_unlock(&mtx);
    if (s != 0)
        errExitEN(s, "pthread_mutex_unlock");
}

上面的代码可以工作,但是会浪费 CPU 时间因为主线程不断循环,检查变量的状态avail。条件变量可以解决这个问题。它允许一个线程休眠(等待),直到另一个线程通知(信号)它必须做某事。


然后这本书给出了他认为更好的版本,带有条件变量:

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int avail = 0;

生产者线程:

/* Code to produce a unit omitted */
s = pthread_mutex_lock(&mtx);
if (s != 0)
    errExitEN(s, "pthread_mutex_lock");

avail++;  /* Let consumer know another unit is available */

s = pthread_mutex_unlock(&mtx);
if (s != 0)
    errExitEN(s, "pthread_mutex_unlock");

s = pthread_cond_signal(&cond); /* Wake sleeping consumer */
if (s != 0)
    errExitEN(s, "pthread_cond_signal");

消费者线程:

for (;;) {
    s = pthread_mutex_lock(&mtx);
    if (s != 0)
        errExitEN(s, "pthread_mutex_lock");
    while (avail == 0) {            /* Wait for something to consume */
        s = pthread_cond_wait(&cond, &mtx);
        if (s != 0)
            errExitEN(s, "pthread_cond_wait");
    }

    while (avail > 0) {             /* Consume all available units */
        /* Do something with produced unit */
        avail--;
    }

    s = pthread_mutex_unlock(&mtx);
    if (s != 0)
        errExitEN(s, "pthread_mutex_unlock");
    /* Perhaps do other work here that doesn't require mutex lock */
}

问题:为什么第一个版本比第二个版本效率低(浪费 CPU 时间)?我看不出区别。

顺便说一句,你能给我一个例子来说明作者的观点吗,我认为是:
与只使用互斥锁的相比,你可以使用互斥锁和条件变量来提高性能。

标签: cmultithreadingunixmutexcondition-variable

解决方案


首先,两者都有问题。您希望生产者在锁外生产项目,并仅使用锁将作业排入队列并通知消费者,并且您希望消费者在锁内等待和出队,但要在外面对作业“做点什么”的锁。

现在,关于条件变量:互斥锁允许您同步两个或多个线程的代码,以确保互斥锁内的代码运行时不会中断,以保持数据完整性:如果 2 个线程同时运行 i++,您有不能保证我会精确地增长 2。所以这里的互斥锁解决了同时从一个队列(或者在这种情况下是avail变量)入队/出队的问题,但是你没有一种机制来真正让消费者睡觉所以不要浪费生产者可以使用的宝贵CPU来生产更多的工作。这就是条件变量帮助消费者睡觉的地方,直到生产者设置avail>0并唤醒消费者。

示例生产者:

while (true) {
    // produce outside the lock
    ...

    pthread_mutex_lock(&mtx);   // lock
    ++avail;                    // update new job 
    pthread_cond_signal(&cond); // wake up consumer
    pthread_mutex_unlock(&mtx); // unlock
}

示例消费者:

while (true) {
    pthread_mutex_lock(&mtx);   // lock
    while (avail<=0)            // wait for new job
        pthread_cond_wait(&cond, &mtx);
    --avail;                    // get the new job 
    pthread_mutex_unlock(&mtx); // unlock

    // consume job outside the lock
}

请注意,为了简化,我删除了错误处理

更多阅读


推荐阅读