c++ - 为什么 std::mutex 需要很长时间才能共享?
问题描述
这段代码演示了互斥锁在两个线程之间共享,但一个线程几乎一直拥有它。
#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 编译。
输出是#
和.
字符的混合,表明互斥体正在共享,但模式令人惊讶。通常是这样的:
.......#####..........................##################......................##
即thread_lock
锁定互斥锁很长时间。几秒甚至几十秒后,接收方main_lock
(短暂地)接收控制权,然后thread_lock
将其取回并保留很长时间。打电话std::this_thread::yield()
不会改变任何事情。
为什么两个互斥锁获得锁定的可能性不同,如何使互斥锁以平衡的方式共享?
解决方案
使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-way fair(甚至 omega-fair),如果有多达 N 个元素在等待,它们都会在释放线程获得另一个机会之前轮到它们。
推荐阅读
- javascript - 我在 Node.js 中创建了一个 MySQL 数据库。如何查看它是否创建?
- javascript - Summernote - 多个编辑器实例上的自定义按钮
- powershell - 如何将 Get-WmiObject 控制台输出写入 PowerShell 中的文件
- swift - 导航后调用 API 发生 SwiftUI 错误
- python - 将 For 循环中的最大值附加到 Python 中的新列表
- mysql - MySQL 5.5 中可以使用什么排序规则对日文字符进行排序?
- vue.js - 使用 Axios 和 VueX 的 Vue 应用程序在 localhost:3000 上被 CORS 阻止?
- flutter - 快照返回 Null
- c++ - 如何修复“gcc:致命错误:无法执行?cc1plus?:execvp:没有这样的文件或目录”?
- javascript - Electron 应用程序错误:ENOENT:没有这样的文件或目录,打开“/.env”