注意: 这不是一个实际的问题(我从未使用过scoped_lock锁定过2个以上的互斥锁),我最好奇的是,为什么以不同顺序锁定互斥锁时,scoped_lock的实现显然会对性能产生较大的影响。
下面的示例代码godbolt link。
#include<mutex>
#include<thread>
#include<chrono>
#include<iostream>
std::mutex m1,m2,m3,m4,m5,m6;
int cnt =0;
void f(){
for (int i=0; i< 500*1000; ++i){
std::scoped_lock sl{m1,m6};
cnt++;
}
}
void f_unord(){
for (int i=0; i< 500*1000; ++i){
std::scoped_lock sl{m4,m6,m1,m3};
cnt++;
}
}
int main(){
for (int run = 0; run<4; ++run)
{
{
const auto start = std::chrono::steady_clock::now();
std::thread t1(f),t2(f);
t1.join();
t2.join();
const auto end = std::chrono::steady_clock::now();
std::cout << "same lock order: " << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count() << std::endl;
std::cout << cnt << std::endl;
}
{
const auto start = std::chrono::steady_clock::now();
std::thread t1(f),t2(f_unord);
t1.join();
t2.join();
const auto end = std::chrono::steady_clock::now();
std::cout << "different lock order: " << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count() << std::endl;
std::cout << cnt << std::endl;
}
}
}
请注意为什么这会令人惊讶:我希望由于互斥体不可移动,因此实现只能按地址对互斥体进行排序并使用锁定顺序。
关于Godbolt基准测试的说明:我知道Godbolt是不可靠的,我在VM中的机器上得到了类似的结果:
g ++ --version; g ++ -O2 -std = c ++ 17 scoped_lock.cpp -pthread; ./a.out
g ++(Ubuntu 9.2.1-9ubuntu2)9.2.1 20191008版权所有(C)2019免费 Software Foundation,Inc。这是免费软件。请参阅源 复制条件。没有保修;甚至没有 特定目的的适销性或适用性。
不同锁定顺序:1074
1000000
相同的锁定顺序:602
2000000
不同的锁定顺序:987
3000000
相同的锁定顺序:612
4000000
不同的锁定顺序:1012
5000000
相同的锁定顺序:585
6000000
不同的锁定顺序:1050
7000000
相同的锁定顺序:675
8000000
不同的锁定顺序:1107
9000000
相同的锁定顺序:609
10000000