锁定boost::unique_lock时抛出的异常,该锁已经锁定在其他线程中

Exception thrown when locking boost::unique_lock which is already locked in other thread

本文关键字:锁定 线程 其他 unique boost lock 异常      更新时间:2023-10-16

存在全局对象boost::unique_lock。一个线程锁定它。当其他线程试图锁定异常时,抛出"已经拥有互斥锁"。为什么会发生这种情况?我希望线程被阻塞,直到其他线程调用unlock。

boost::mutex mutex;
boost::unique_lock<boost::mutex> lock(mutex);
static void* scheduller(void* arg)
{
    boost::this_thread::sleep(boost::posix_time::seconds(5));
    lock.lock();    
    return 0;
}
static void* usb(void* arg)
{
    lock.lock();
    boost::this_thread::sleep(boost::posix_time::seconds(20));
    return 0;
}
int BEvent_test()
{
    lock.unlock();
    int a = 0;
    boost::thread thread1(usb,&a);
    boost::thread thread2(scheduller,&a);
    boost::this_thread::sleep(boost::posix_time::seconds(100));
    return 0;
}

在BEvent_test函数中,我正在做解锁,因为唯一的锁被锁定在构造函数中。延迟确保调度器线程在usb线程之后启动。当它尝试执行lock()时抛出异常。为什么?

您对unique_lock的使用是错误的,特别是那些应该在不同线程之间共享的对象,因为它们不是线程安全的。是的,这听起来很奇怪,但它确实有意义,因为线程安全和提供线程安全是两个不同的东西。

unique_lock的思想是帮助您管理互斥锁上的锁。为此,您只能在本地创建这些对象。这也是它最初被锁定的原因,你似乎可以通过解锁它来"解决"问题。但是,您应该尝试使用它们的作用域/生命周期来管理锁。

现在,你说你需要一个unique_lock来使用一个事件。是也不是。您需要一个,但不是每个使用该事件的线程都需要一个。相反,您可以使用来自不同线程的不同本地锁实例。顺便说一句:事件需要锁的原因是在等待事件时,锁必须被释放。在接收到事件(或超时)之后,它还必须重新获取锁。换句话说,锁充当互斥锁的代理,提供间接和一些额外的保证。

您收到异常的原因是您尝试锁定unique_lock两次。我不认为这是来自同一条线索的问题,我甚至不会说它真的来自同一条线索。然而,当你锁定一个互斥锁时(通过unique_lock),你说你现在进入了一个临界区。这意味着之前,你不在临界区。如果unique_lock现在发现锁已经持有,这意味着代码中的某些地方出错了,因为代码似乎混淆了它是否在临界区内。因此,您会得到一个异常作为修复代码的动力。

我不熟悉boost同步。从这里的文档中,在我看来,这个想法是创建unique_lock对象的代码,以便在给定的持续时间(时间,unique_lock的生命周期等)内拥有mutex的所有权。我认为你应该尝试锁定mutex,而不是其他线程中的unique_lock

在我看来,unique_lock对于在方法中锁定mutex是有用的,并且知道无论发生什么,在方法退出(unique_lock销毁)时,互斥锁将被释放。您不必确保捕获异常等以便释放mutex

消息不是表示您在另一个线程上锁定了互斥锁。这意味着您已经在同一个线程上锁定了互斥锁。由于互斥对象不是递归可锁的,这是一个错误。


你正在手动锁定/解锁互斥锁。

从所示的代码片段中很难看出,但是

很容易
  • 做错了(忘记及时解锁)
  • 几乎不可能使其异常安全(所示代码肯定不是)。

相反,你应该使用scoped_lock(或lock_guard/unique_lock)以异常安全的方式自动平衡锁/解锁。


重读你的代码,你只是使用unique_lock完全错误:

Live On Coliru

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread.hpp>
#include <iostream>
#define TRACE(msg) do { std::cout << msg << " " << __FUNCTION__ << " at " << relative_ms() << "msn"; } while(false)
int relative_ms() {
    using boost::posix_time::second_clock;
    static auto t0 = second_clock::local_time();
    return (second_clock::local_time() - t0).total_milliseconds();
}
boost::mutex mutex;
static void scheduler()
{
    boost::unique_lock<boost::mutex> lock(mutex);
    TRACE("Enter");
    boost::this_thread::sleep_for(boost::chrono::seconds(2));
    TRACE("Leave");
}
void usb()
{
    boost::unique_lock<boost::mutex> lock(mutex);
    TRACE("Enter");
    boost::this_thread::sleep_for(boost::chrono::seconds(5));
    TRACE("Leave");
}
int main()
{
    TRACE("Enter");
    boost::thread thread1(usb);
    boost::thread thread2(scheduler);
    boost::this_thread::sleep_for(boost::chrono::seconds(10));
    TRACE("Leave");
}

打印。

Enter main at 0ms
Enter usb at 0ms
Leave usb at 5000ms
Enter scheduler at 5000ms
Leave scheduler at 7000ms
Leave main at 10000ms

Enter main at 0ms
Enter scheduler at 0ms
Leave scheduler at 2000ms
Enter usb at 2000ms
Leave usb at 7000ms
Leave main at 10000ms

仅取决于哪个线程先获取互斥锁