为什么这个使用互斥锁的例子与另一个带有附加条件变量的例子相比效率较低? [英] Why this example using mutex is less efficient compared to another one with additional condition variable?

查看:64
本文介绍了为什么这个使用互斥锁的例子与另一个带有附加条件变量的例子相比效率较低?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

来自Linux 编程接口的示例:

<块引用>

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

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;静态 int 效用 = 0;/* 生成单元的代码省略 */s = pthread_mutex_lock(&mtx);如果(s!= 0)errExitEN(s, "pthread_mutex_lock");有用++;/* 让消费者知道另一个单元可用 */s = pthread_mutex_unlock(&mtx);如果(s!= 0)errExitEN(s, "pthread_mutex_unlock");

<块引用>

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

for (;;) {s = pthread_mutex_lock(&mtx);如果(s!= 0)errExitEN(s, "pthread_mutex_lock");而(可用> 0){/* 消耗所有可用的单位 *//* 对生产单元做一些事情 */有用--;}s = pthread_mutex_unlock(&mtx);如果(s!= 0)errExitEN(s, "pthread_mutex_unlock");}

<块引用>

上面的代码有效,但是浪费了CPU时间因为主线程不断循环,检查变量avail的状态.一种条件变量解决了这个问题.它允许线程休眠(等待)直到另一个线程通知(信号)它必须这样做东西.

<小时>

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

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;静态 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;静态 int 效用 = 0;

生产者线程:

/* 生成单元的代码省略 */s = pthread_mutex_lock(&mtx);如果(s!= 0)errExitEN(s, "pthread_mutex_lock");有用++;/* 让消费者知道另一个单元可用 */s = pthread_mutex_unlock(&mtx);如果(s!= 0)errExitEN(s, "pthread_mutex_unlock");s = pthread_cond_signal(&cond);/* 唤醒睡眠消费者 */如果(s!= 0)errExitEN(s, "pthread_cond_signal");

消费者线程:

for (;;) {s = pthread_mutex_lock(&mtx);如果(s!= 0)errExitEN(s, "pthread_mutex_lock");while (avail == 0) {/* 等待一些东西被消耗 */s = pthread_cond_wait(&cond, &mtx);如果(s!= 0)errExitEN(s, "pthread_cond_wait");}while (avail > 0) {/* 消耗所有可用单元 *//* 对生产单元做一些事情 */有用--;}s = pthread_mutex_unlock(&mtx);如果(s!= 0)errExitEN(s, "pthread_mutex_unlock");/* 也许在这里做其他不需要互斥锁的工作 */}

<小时>

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

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

解决方案

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

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

示例生产者:

while (true) {//在锁外产生...pthread_mutex_lock(&mtx);//锁++有效;//更新新作业pthread_cond_signal(&cond);//唤醒消费者pthread_mutex_unlock(&mtx);//开锁}

示例消费者:

while (true) {pthread_mutex_lock(&mtx);//锁while (avail<=0)//等待新作业pthread_cond_wait(&cond, &mtx);--有效;//获取新工作pthread_mutex_unlock(&mtx);//开锁//在锁外消费作业}

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

更多阅读

An example from The Linux Programming Interface:

In the producer threads, we would have code such as the following:

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");

And in the main (consumer) thread, we could employ the following code:

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");
}

The above code works, but it wastes CPU time, because the main thread continually loops, checking the state of the variable avail. A condition variable remedies this problem. It allows a thread to sleep (wait) until another thread notifies (signals) it that it must do something.


The book then gives what he thinks is a better version, with condition variable:

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

Producer thread:

/* 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");

Consumer thread:

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 */
}


Question: Why the first version is less effcient(wastes CPU time) compared to the second one? I can't see the difference.

Btw, could you give me an example which may illustrate the author's point of view, which I think is:
You can use mutex along with condition variable to improve perfomance, compared to the one that uses only mutex.

解决方案

To start off, both are problematic. You want the producer to produce items outside the lock, and use the lock only to enqueue the job and notify the consumer about it, and you weant the consumer to wait and dequeue inside the lock, but to "do something" with the job OUTSIDE of the lock.

Now, regarding the condition variable: A mutex allows you to synchronize the code of two or more threads to ensure that the code within the mutex runs with no interruptions in order to maintain data integrity: if 2 threads run i++ at the same time you have no guarantee that i will grow exactly by 2. So the mutex here solves the problem of enqueuing/dequeuing from one queue (or in this case the avail variable) at the same time, but you do not have a mechanism to actually let the consumer sleep so not to waste precious CPU that can be used by the producer in order to produce more work. This is where the condition variable comes to help the consumer to sleep until the producers sets avail>0 and wakes the consumer up.

Example producer:

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
}

Example consumer:

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
}

Note that I have removed error handling for simplification

For more reading

这篇关于为什么这个使用互斥锁的例子与另一个带有附加条件变量的例子相比效率较低?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆