c++ 传递锁所有权

qltillow  于 2023-11-19  发布在  其他
关注(0)|答案(4)|浏览(138)

对于RAII,我们有std::unique_lockstd::scoped_lock。这两个是显式可移动的。RAII对象在构造时是“锁定”的,在析构时是“解锁”的。所以,我认为绝对没有真实的理由禁止“所有权”传递给不同的线程。如果它不正确或有用,那么可以说传递unique_ptr给另一个线程也是一样的。
我想做一些事情,比如:

std::scoped_lock lock{mutex};
// Do some stuff.
// Decide to multithread...
auto lambda = [lock = std::move(lock)]
{
  // Do stuff with the lock.
};
std::thread{std::move(lambda)}.detach();

字符串
我有两个问题
1.我错过了什么?这不合理吗?
1.在cppreference's description of the std::scoped_lock的不同线程中,我没有看到任何关于不解锁的内容,所以我想知道标准是否禁止我上面发布的代码。

cbeh67ev

cbeh67ev1#

“不要这样做”的真实的原因是,这是非常规的,如果你曾经期望其他程序员阅读你的代码,如果你曾经期望与他们合作,或者如果你曾经向另一个程序员寻求帮助,那么如果每个人都说同样的语言,你会有更好的运气。
“Mutex”不仅仅是某个编程语言中的一个类型的名称,它还是一个design pattern的名称。如果你在代码中使用了“mutex”,那么所有看到它的人都会立即期望你以某种方式使用它。如果你违背了他们的期望,他们会感到困惑和沮丧。他们不会喜欢和你一起工作或帮助你。
你想做的事情有一个不同的名字,叫做binary semaphore,它的行为几乎和互斥锁一样,除了,如果你在一个线程中锁定它,在另一个线程中解锁它,没有人会惊讶。

  • “Mutex”说,“我只是要访问这里的一些共享数据,我会尽可能快地完成它。”

“信号量”表示“Hey! Hold my beer, and watch this

yb3bgrhw

yb3bgrhw2#

TL;DR

你完全可以把一个 lock 从一个线程移到另一个线程,只要底层的 Lockable 支持它。
所以,我认为绝对没有真实的理由禁止“所有权”被传递到不同的线程。
原则上,我也没有,但有几个警告:
1.如果标准是保守的,或者是限制性的,或者是希望为一些巧妙的特定于平台的优化提供实现空间,那么即使它有意义,它也可能是UB
1.一些特定的互斥体类型,比如递归互斥体,可能需要存储当前线程ID,因此无法科普这种用例。
所有这些都说,我们可以检查标准:
[thread.req.lockable.general]一般情况下
1/执行代理是一个实体,例如可以与其他执行代理并行执行工作的线程。
[Note 1:实现或用户可以引入其他类型的代理,如进程或线程池任务。 
[Note 2:一些可锁定对象是“agent oblivious”,因为它们适用于任何执行代理模型,因为它们不确定或存储代理的ID(例如,普通的自旋锁)。
所以,听起来我们可以自由地使用自己的“执行代理”抽象,并且至少一些可锁定类型应该可以在我们的抽象从一个线程移动到另一个线程时正常工作。
不幸的是,没有一个标准互斥类型属于这一类:它们都指定必须由“拥有”互斥的线程解锁,拥有互斥的唯一方法是锁定它,并且没有描述转移所有权的机制。
但是,您可以基于信号量、自旋锁或其他任何没有明确禁止的东西编写自己的 * Cpp 17 Lockable * 类型,并认为自己受到了标准的正式认可。

3xiyfsfu

3xiyfsfu3#

对于RAII,我们有std::unique_lockstd::scoped_lock。这两个是显式可移动的。RAII对象在构造时被“锁定”,在析构时被“解锁”。所以,我认为绝对没有真实的理由禁止“所有权”传递给不同的线程。
是的,就std::unique_lockstd::scoped_lock而言,将所有权转移到另一个线程是没有问题的。这是因为这些类focus on one task- RAII用于锁。
这些类完全不知道互斥体是如何操作的。(例如互斥)是“(Basic)Lockable“- 它有成员函数lock()unlock(),在某些情况下还有try_lock()。锁类确保对lock()的调用与对unlock()的调用相匹配,对try_lock()的调用与对unlock()的调用相匹配。这些类不关心这些函数的效果是什么;重要的是在需要时调用unlock()
你应该看看 Package 的是什么,而不是看RAII Package 器。如果你 Package 的是std::mutex,那么以下内容适用。
一个调用线程从它成功调用locktry_lock开始,直到它调用unlock为止,都拥有一个mutex
这就是为什么你不能将std::mutex的所有权转移到另一个线程。锁定std::mutex的线程将拥有互斥锁,直到调用unlock。不仅仅是直到unlock被调用(被某人),而是直到特定的线程调用unlock。当使用std::mutex时,这个任务不能委托给另一个线程。
另一个Lockable可能会有不同的行为,允许所有权跨线程转移,但std::mutex不会。

lyr7nygr

lyr7nygr4#

补充一下前面的答案:必须从调用lock的线程调用unlock的原因是因为pthread_mutex_unlock或它的Windows等价物有这个约束。最后,它实际上是互斥量定义的一部分。

相关问题