c - 为什么这个使用互斥锁的例子比另一个带有额外条件变量的例子效率低?
问题描述
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 时间)?我看不出区别。
顺便说一句,你能给我一个例子来说明作者的观点吗,我认为是:
与只使用互斥锁的相比,你可以使用互斥锁和条件变量来提高性能。
解决方案
首先,两者都有问题。您希望生产者在锁外生产项目,并仅使用锁将作业排入队列并通知消费者,并且您希望消费者在锁内等待和出队,但要在外面对作业“做点什么”的锁。
现在,关于条件变量:互斥锁允许您同步两个或多个线程的代码,以确保互斥锁内的代码运行时不会中断,以保持数据完整性:如果 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
}
请注意,为了简化,我删除了错误处理
推荐阅读
- javascript - 为什么 pm2 忽略了生态系统.config.js 文件中传递给节点的--experimental-modules?
- java - 使用 AES/GCM (Android 9) 时,Java Cipher.update 不会写入缓冲区
- node.js - Amazon SES SendRawEmail (Node.JS) 中的电子邮件未显示 To:、Cc 和 Bcc
- java - Selenium 中的 Actions 类不建议可用的方法
- amazon-web-services - TLS 不适用于 Kubernetes 中的 LoadBalancer 支持的服务
- php - AJAX 请求未从 PHP 类返回数据
- powershell - Powershell 源代码编码/隐藏字符
- reactjs - 如何将一个道具作为变量传递给 React 中的另一个道具?
- python - 无法使用 python 在另一个图像上广播图像
- javascript - 为什么不能正确读取用户输入和选择?