- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
这段代码演示了互斥锁在两个线程之间共享,但是围绕 thread_mutex
的作用域 block 发生了一些奇怪的事情。
(我在 another question 中有此代码的变体,但这似乎是第二个谜。)
#include <thread>
#include <mutex>
#include <iostream>
#include <unistd.h>
int main ()
{
std::mutex m;
std::thread t ([&] ()
{
while (true)
{
{
std::lock_guard <std::mutex> thread_lock (m);
usleep (10*1000); // or whatever
}
std::cerr << "#";
std::cerr.flush ();
}
});
while (true)
{
std::lock_guard <std::mutex> main_lock (m);
std::cerr << ".";
std::cerr.flush ();
}
}
这基本上是有效的,但围绕 thread_lock
的范围 block 在理论上应该不是必需的。但是,如果您将其注释掉...
#include <thread>
#include <mutex>
#include <iostream>
#include <unistd.h>
int main ()
{
std::mutex m;
std::thread t ([&] ()
{
while (true)
{
// {
std::lock_guard <std::mutex> thread_lock (m);
usleep (10*1000); // or whatever
// }
std::cerr << "#";
std::cerr.flush ();
}
});
while (true)
{
std::lock_guard <std::mutex> main_lock (m);
std::cerr << ".";
std::cerr.flush ();
}
}
输出是这样的:
........########################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
也就是说,thread_lock
似乎永远不会屈服于 main_lock
。
为什么 thread_lock
总是获得锁而 main_lock
总是等待,如果冗余作用域 block 被删除?
最佳答案
我在带有 GCC (7.3.0) 的 Linux 上使用 pthreads 测试了您的代码(删除了 block 作用域)并得到了与您相似的结果。主线程处于饥饿状态,但如果我等待的时间足够长,我偶尔会看到主线程做一些工作。
但是,我在 Windows 上使用 MSVC (19.15) 运行了相同的代码,没有线程被饿死。
看起来您使用的是 posix,所以我猜您的标准库在后端使用 pthreads? (即使使用 C++11,我也必须链接 pthreads。)Pthreads 互斥量不能保证公平性。但这只是故事的一半。您的输出似乎与 usleep
调用有关。
如果我取出 usleep
,我会看到公平 (Linux):
// fair again
while (true)
{
std::lock_guard <std::mutex> thread_lock (m);
std::cerr << "#";
std::cerr.flush ();
}
我的猜测是,由于持有互斥量时 sleep 时间过长,因此实际上可以保证主线程将尽可能地阻塞。想象一下,一开始主线程可能会尝试自旋,希望互斥锁很快可用。一段时间后,它可能会被列入等候名单。
在辅助线程中,lock_guard
对象在循环结束时被销毁,从而释放互斥量。它会唤醒主线程,但会立即构造一个新的 lock_guard
再次锁定互斥体。主线程不太可能会抢到互斥锁,因为它刚刚被调度。所以除非在这个小窗口发生上下文切换,否则辅助线程很可能会再次获得互斥量。
在带有作用域 block 的代码中,辅助线程中的互斥量在IO调用之前被释放。打印到屏幕需要很长时间,因此主线程有足够的时间来获取互斥量。
正如@Ted Lyngmo 在他的回答中所说,如果您在创建 lock_guard
之前添加 sleep ,则饥饿的可能性会大大降低。
while (true)
{
usleep (1);
std::lock_guard <std::mutex> thread_lock (m);
usleep (10*1000);
std::cerr << "#";
std::cerr.flush ();
}
我也用 yield 试过这个,但我需要 5+ 才能让它更公平,这让我相信在实际的库实现细节、操作系统调度程序以及缓存和内存子系统效果中还有其他细微差别。
顺便说一句,感谢您提出一个很好的问题。它真的很容易测试和使用。
关于c++ - 为什么冗余的额外作用域 block 会影响 std::lock_guard 行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53068844/
今天有小伙伴给我留言问到,try{...}catch(){...}是什么意思?它用来干什么? 简单的说 他们是用来捕获异常的 下面我们通过一个例子来详细讲解下
我正在努力提高网站的可访问性,但我不知道如何在页脚中标记社交媒体链接列表。这些链接指向我在 facecook、twitter 等上的帐户。我不想用 role="navigation" 标记这些链接,因
说现在是 6 点,我有一个 Timer 并在 10 点安排了一个 TimerTask。之后,System DateTime 被其他服务(例如 ntp)调整为 9 点钟。我仍然希望我的 TimerTas
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我就废话不多说了,大家还是直接看代码吧~ ? 1
Maven系列1 1.什么是Maven? Maven是一个项目管理工具,它包含了一个对象模型。一组标准集合,一个依赖管理系统。和用来运行定义在生命周期阶段中插件目标和逻辑。 核心功能 Mav
我是一名优秀的程序员,十分优秀!