繁体   English   中英

当单个线程获取同一个互斥锁的 2 个 unique_lock 时,unique_lock 是什么意思?

[英]what does unique_lock mean when a single thread acquire 2 unique_lock of the same mutex?

我有以下代码,来自https://en.cppreference.com/w/cpp/thread/unique_lock 但是,在打印输出时,我看到了一些意想不到的结果,并希望得到一些解释。

代码是:

#include <mutex>
#include <thread>
#include <chrono>
#include <iostream>
 
struct Box {
    explicit Box(int num) : num_things{num} {}
 
    int num_things;
    std::mutex m;
};
 
void transfer(Box &from, Box &to, int anotherNumber)
{
    // don't actually take the locks yet
    std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);
    std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);
 
    // lock both unique_locks without deadlock
    std::lock(lock1, lock2);
 
    from.num_things += anotherNumber;
    to.num_things += anotherNumber;
    
    std::cout<<std::this_thread::get_id()<<" "<<from.num_things<<"\n";
    std::cout<<std::this_thread::get_id()<<" "<<to.num_things<<"\n";
    // 'from.m' and 'to.m' mutexes unlocked in 'unique_lock' dtors
}
 
int main()
{
    Box acc1(100);   //initialized acc1.num_things = 100
    Box acc2(50);    //initialized acc2.num_things = 50
 
    std::thread t1(transfer, std::ref(acc1), std::ref(acc2), 10);
    std::thread t2(transfer, std::ref(acc2), std::ref(acc1), 5);
 
    t1.join();
    t2.join();
}

我的期望:

  1. acc1 将使用 num_things=100 进行初始化,acc2 将使用 num_things=50 进行初始化。
  2. 说线程 t1 首先运行,它获取互斥锁 m,有 2 个锁。 一旦锁被锁定,并且可以将 num_things 分配给 num=10
  3. 完成后,它将按顺序打印 from.num_things = 110 和 to.numthings = 60。 先“从”,后“到”。
  4. thread1 完成代码的临界区,包装器 unique_lock 调用其析构函数,基本上解锁互斥锁。

这是我不明白的地方

我希望先解锁 lock1 填充,然后再解锁 lock2。 线程 t2 然后以相同的顺序获取互斥锁并先锁定 lock1,然后锁定 2。 它还将按顺序运行关键代码直到 cout。

线程 t2 将从 t1 中获取全局 acc1.num_things = 110 和 acc2.num_things = 60。

我希望 t2 将首先打印 from.num_things = 115,然后打印 to.numthings = 65。

然而,经过无数次的试验,我总是得到相反的顺序。 这就是我的困惑。

在此处输入图片说明

我希望先解锁 lock1 填充,然后再解锁 lock2。

不,反之亦然。 在您的函数中,首先构造lock1 ,然后lock2 因此,当函数返回时, lock2被销毁,然后lock1 ,因此lock2的析构函数在lock1的析构函数之前释放其锁。

std::lock设法获取多个锁的实际顺序与锁如何被销毁以及释放它们对各自互斥锁的所有权无关。 这样做仍然遵循正常的 C++ 规则。

说线程 t1 首先运行,

无论如何,您无法保证这一点。 在上面的代码中, t2完全有可能首先进入函数并获取互斥锁上的锁。 而且,也完全有可能每次运行这个程序时,您都会得到不同的结果, t1t2随机赢得比赛。

在不涉及技术上的笨拙的情况下,C++ 唯一能保证的是std::thread在线程函数在新的执行线程中被调用之前被完全构造。 您无法保证在一个接一个地创建两个执行线程时,第一个将调用其函数并在第二个执行线程执行相同操作之前运行线程函数的任意部分。

所以完全有可能t2偶尔会得到锁上的第一个 dib。 或者,总是。 试图控制跨执行线程的事件的相对顺序比您想象的要困难得多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM