【问题标题】:C++11 pass std::unique_lock<std::mutex> to lambdaC++11 将 std::unique_lock<std::mutex> 传递给 lambda
【发布时间】:2014-08-21 14:13:36
【问题描述】:

我有一些函数试图锁定std::mutex,如果互斥锁成功锁定,函数会创建std::thread,使用lambda-function 作为线程函数参数,并使用std::move() 传递锁定:

static std::mutex mtx;
// some mutex defended stuff

void someFunc() {
    // try lock mutex using unique_lock
    std::unique_lock<std::mutex> lock(mtx, std::try_to_lock);
    if(!lock.owns_lock()) {
        return; // locking fails
    }

    // task for thread
    auto task = [&](std::unique_lock<std::mutex>&& lock) {
        // do async work and release lock when it done
        lock.unlock();

        // do something after releasing lock
    };

    // create thread, pass lock 
    std::thread taskThread(task, std::move(lock));
    taskThread.detach();
}

我遇到编译器错误:

<lambda_1918cc58d906c210588b1a8bb33f1b0d>::operator
()(std::unique_lock<_Mutex> &&) const' : cannot convert parameter 1 from
'std::unique_lock<_Mutex>' to 'std::unique_lock<_Mutex> &&'

我应该如何正确地将std::unique_lock 传递给我的 lambda?

统一更新: 实际上这个函数是一个原生的 C API libaray 调用,它尝试异步启动一些库操作并在任何情况下立即返回控制,返回一些返回码给调用者。此代码已简化以专注于特定问题。

【问题讨论】:

  • 你想用这个实现什么?您可能不应该将lock 传递到线程中。它已经可以访问“mtx”,这正是它所需要的。
  • 始终保持锁定真的很重要吗?我的意思是,如果等待结果的人不认为启动异步任务及其执行的步骤是原子的,您是否会冒数据损坏的风险?在我看来,您可能想要这样做,因为您使用互斥锁的锁定状态来监视异步执行的状态,这是一种反模式。
  • @Christophe 如果他这样做了,那么 somefunc() 中的 lambda 和 unique_lock 都会 unlock() 互斥体,这是未定义的行为。
  • 嗯,我刚刚意识到你的代码即使编译也有未定义的行为。您正在从不同的线程锁定和解锁 mutex,这是不允许的。解锁它的线程必须与锁定它的线程相同。无论如何,由于this bug,您的代码无法在 VC12 上编译

标签: c++ multithreading c++11 concurrency lambda


【解决方案1】:

您在 gcc 和 clang 上的代码 compiles。它无法在 VS2013 (VC12) 上编译,因为实现中的 a bug 需要 std::thread 构造函数的参数是可复制的。这会导致编译器尝试复制 std::unique_lock,这当然会失败。

但是,您的代码具有未定义的行为,因为您在两个不同的线程中锁定和解锁 mutex,这是不允许的。解锁它的线程必须与之前锁定它的线程相同。

【讨论】:

  • 您能建议任何有关在不同线程中锁定/解锁的解决方法吗?
  • @vard 您也许可以使用std::future 和std::promise 在稍后等待工作线程后让原始线程解锁互斥锁,但我对那部分了解不够标准库提出更具体的建议。
  • 使用该原语的主线程将被阻塞,直到未来的结果准备好,AIK。不幸的是,我需要在创建线程后立即将控制权返回给调用者。我可以尝试在线程中锁定互斥体并使用条件变量来通知主线程关于锁定结果。
  • @vard 是的,您也许可以使用condition_variable 方法来工作。另一种可能性是,如果somefunc() 在获取锁和启动线程之间不需要在锁下做任何事情,那么您可以将锁获取移到线程任务中。如果无法这样做,它将尝试获取锁并提前返回。语义与您的示例略有不同,但也许这仍然适用于您。
猜你喜欢
  • 2013-12-29
  • 2012-11-02
  • 2014-01-16
  • 1970-01-01
  • 2012-10-17
  • 2020-12-31
  • 1970-01-01
  • 2022-01-19
相关资源
最近更新 更多