将std::unique_lock<std::mutex>传递给lambda的C++11

4

我有一些函数,试图锁定std::mutex,如果成功锁定,则该函数创建具有lambda函数作为线程函数参数的std::thread,并且还使用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库函数调用,它尝试异步启动一些库操作,并在任何情况下立即返回控制,向调用者返回一些返回代码。这个代码被简化以便集中讨论具体问题。


你使用的编译器和版本是什么?你的代码可以在gcc-4.9和clang-3.4上编译 - Praetorian
1
@Christophe 如果这样做,somefunc() 中的 lambda 表达式和 unique_lock 都会 unlock() 互斥量,这是未定义的行为。 - Praetorian
@Slava std::unique_lock 可以只使用移动语义传递,不是吗? - vard
@vard 如何传递右值引用在我之前提到的主题中已经得到了回答。但是还有另一种解决方案,可以传递此锁(例如使用std::shared_ptr)。 - Slava
2
嗯,我刚意识到即使您的代码能够编译,它仍然存在未定义的行为。您正在不同线程中锁定和解锁mutex,这是不允许的。解锁它的线程必须与锁定它的线程相同。无论如何,您的代码在VC12上由于此错误而无法编译(链接:https://connect.microsoft.com/VisualStudio/feedback/details/729886). - Praetorian
显示剩余13条评论
1个回答

9

您的代码在gcc和clang上编译通过。但是在VS2013(VC12)上因为一个实现上的错误,导致std::thread构造函数的参数需要可以被拷贝,从而编译失败。这会导致编译器试图拷贝std::unique_lock,当然是失败的。

不过,您的代码存在未定义行为,因为您在两个不同的线程中对mutex进行了锁定和解锁操作,这是不允许的。解锁它的线程必须与之前锁定它的线程相同。


你能否提供关于在不同线程中锁定/解锁的任何解决方法? - vard
@vard 你可能可以使用 std::futurestd::promise,在等待工作线程后,让原始线程解锁互斥量,但我对标准库的这部分了解不够,无法提供更具体的建议。 - Praetorian
1
使用这些原语,主线程将被阻塞,直到未来的结果准备就绪,AIK。不幸的是,在创建线程后,我需要立即将控制权返回给调用者。我可以尝试在线程中锁定互斥量,并使用条件变量通知主线程有关锁定结果的信息。 - vard
@vard 是的,你可能能够让 condition_variable 方法起作用。另一个可能性是,如果 somefunc() 在获取锁和启动线程之间不需要在锁下执行任何操作,则可以将锁获取移动到线程任务中。然后它将尝试获取锁并在无法获取时提前返回。语义与您的示例略有不同,但也许对您仍然有效。 - Praetorian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接