为什么std::互斥需要很长的、非常不规则的时间来共享
Why is std::mutex taking a long, highly irregular amount of time to be shared?
这段代码演示了互斥体在两个线程之间共享,但一个线程几乎一直都有它。
#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);
sleep (1); // or whatever
}
std::cerr << "#";
std::cerr.flush ();
}
});
while (true)
{
std::lock_guard <std::mutex> main_lock (m);
std::cerr << ".";
std::cerr.flush ();
}
}
在Ubuntu 18.04 4.15.0-23-generic.上使用g++7.3.0编译
输出是#
和.
字符的混合,表明互斥被共享,但这种模式令人惊讶。通常是这样的:
.......#####..........................##################......................##
即CCD_ 3将互斥锁锁定非常的长时间。几秒甚至几十秒后,main_lock
接收到控制(短暂(,然后thread_lock
将其收回并保持很长时间。调用std::this_thread::yield()
不会更改任何内容。
为什么这两个互斥体获得锁的可能性不一样,我如何使互斥体以平衡的方式共享?
std::mutex
的设计并不公平。它不能保证锁的顺序得到保持,你要么幸运地得到了锁,要么没有。
如果你想要更公平,可以考虑使用std::condition_variable
,比如
#include <thread>
#include <mutex>
#include <iostream>
#include <condition_variable>
#include <unistd.h>
int main ()
{
std::mutex m;
std::condition_variable cv;
std::thread t ([&] ()
{
while (true)
{
std::unique_lock<std::mutex> lk(m);
std::cerr << "#";
std::cerr.flush ();
cv.notify_one();
cv.wait(lk);
}
});
while (true)
{
std::unique_lock<std::mutex> lk(m);
std::cerr << ".";
std::cerr.flush ();
cv.notify_one();
cv.wait(lk);
}
}
使std::mutex
公平是有成本的。在C++中,你不会为你没有要求的东西付费。
你可以写一个锁定对象,其中释放锁的一方不能是下一个获得它的一方。更高级的是,你可以写这样一个对象,只有当其他人在等待时才会发生这种情况。
以下是一个未经测试的公平互斥的快速尝试:
struct fair_mutex {
void lock() {
auto l = internal_lock();
lock(l);
}
void unlock() {
auto l = internal_lock();
in_use = false;
if (waiting != 0) {
loser=std::this_thread::get_id();
} else {
loser = {};
}
cv.notify_one();
}
bool try_lock() {
auto l = internal_lock();
if (in_use) return false;
lock(l);
return true;
}
private:
void lock(std::unique_lock<std::mutex>&l) {
++waiting;
cv.wait( l, [&]{ return !in_use && std::this_thread::get_id() != loser; } );
in_use = true;
--waiting;
}
std::unique_lock<std::mutex> internal_lock() const {
return std::unique_lock<std::mutex>(m);
}
mutable std::mutex m;
std::condition_variable cv;
std::thread::id loser;
bool in_use = false;
std::size_t waiting = 0;
};
这是"公平的",因为如果有两个线程争夺一个资源,它们将轮流进行。如果有人在等锁,任何放弃锁的人都不会再抢了。
然而,这是线程化代码。所以我可能会把它读一遍,但我不会相信我第一次尝试写任何东西。
你可以(以增加的成本(将其扩展为n路公平(甚至ω公平(,如果有多达n个元素在等待,那么在发布线程获得另一个机会之前,它们都会轮到自己。
相关文章:
- OpenGL大的3D纹理(>2GB)非常慢
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 为什么std::互斥需要很长的、非常不规则的时间来共享
- G++ C++17 类模板参数推导在非常特殊的情况下不起作用
- 使用浮点数和双精度数的非常小数字的数学
- 如何删除 LLVM 中的不规则分支?
- 在打开多个其他窗口时使用全屏窗口时帧速率非常低
- 我从int x[3]得到的一个非常奇怪的输出;
- 反向迭代器在C++中非常奇怪的行为
- 将非常大的 int 转换为双倍,在某些计算机上会损失精度
- 我的自定义使用 std::unordered_map 的性能非常慢
- ifstream 尝试读取 9 到 13 之间的无符号字符时非常奇怪的行为
- 一个非常简单的win32套接字代码,但工作错误
- 非常快速地阅读 CSV 文件
- max_element() 给出非常奇怪的错误消息
- 将 mmap 内存用于开销非常低的循环缓冲区
- 内存映射文件访问非常慢
- 如何通过函数参数返回指向不规则数组的指针
- 超过CPU时间限制:当MPI_Sent一个非常大的int*时
- QtWebengine 5.7.1的性能非常慢