史上最简单的Mutex.这个例子行得通吗?它是线程安全的吗
Simplest Mutex ever. Does this example work? Is it thread-safe?
我想问一下有史以来最简单的多线程Mutex方法。以下代码是线程安全的吗(quick-n-dirty)?
class myclass
{
bool locked;
vector<double> vals;
myclass();
void add(double val);
};
void myclass::add(double val)
{
if(!locked)
{
this->locked = 1;
this->vals.push_back(val);
this->locked = 0;
}
else
{
this->add(val);
}
}
int main()
{
myclass cls;
//start parallelism
cls.add(static_cast<double>(rand()));
}
这行得通吗?它是线程安全的吗?我只是想了解如何编写最简单的互斥。
如果你对我的例子有什么建议,那就太好了。
谢谢。
谢谢你说它不起作用。你能建议一个独立于编译器的修复程序吗?
它是线程安全的吗?
当然不是。如果一个线程在检查和设置锁之间被抢占,那么第二个线程可以获取该锁;如果控制权回到第一个线程,那么两个线程都会获得它。(当然,在现代处理器上,两个或多个内核可以同时执行相同的指令,以获得更多的乐趣。)
至少,您需要一个原子测试和设置操作来实现这样的锁。C++11库提供了这样一个东西:
std::atomic_flag locked;
if (!locked.test_and_set()) {
vals.push_back(val);
locked.clear();
} else {
// I don't know exactly what to do here;
// but recursively calling add() is a very bad idea.
}
或者更好:
std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex);
vals.push_back(val);
如果您有一个较旧的实现,那么您将不得不依赖任何可用的扩展/库,因为当时的语言或标准库没有任何帮助。
if(!locked)
和
this->locked = 1;
如果在这两个语句之间有上下文切换,那么锁定机制就会崩溃。您需要一个原子test and set
指令,或者简单地使用现有的mutex
。
此代码不提供vals
矢量的原子修改。考虑以下场景:
//<<< Suppose it's 0
if(!locked)
{ //<<< Thread 0 passes the check
//<<< Context Switch - and Thread 1 is also there because locked is 0
this->locked = 1;
//<<< Now it's possible for one thread to be scheduled when another one is in
//<<< the middle of modification of the vector
this->vals.push_back(val);
this->locked = 0;
}
这行得通吗?它是线程安全的吗?
没有。它有时会失败。
只有当其他线程在执行以下两行之间从不执行任何操作时,互斥锁才会工作:
if(!locked)
{
this->locked = 1;
而你并没有确保这一点。
要了解如何编写互斥,请参阅这篇SO文章。
考虑两个线程或多或少同时运行myclass::add
。此外,假设.locked
的值为false
。
第一个线程执行到并包括以下行:
if(!locked)
{
现在假设系统将上下文切换到第二个线程。它也执行到同一行。
现在我们有两个不同的线程,都认为它们具有独占访问权,并且都在if.的!locked
条件内
它们将或多或少地同时调用vals.push_back()
。
轰。
其他人已经展示了互斥锁是如何失败的,所以我不会重新散列他们的点。我只想补充一件事:最简单的互斥实现比您的代码复杂得多。
如果你对细节感兴趣(或者即使你不感兴趣——这是每个软件开发人员都应该知道的东西),你应该看看Leslie Lamport的Bakery Algorithm,然后从那里开始。
您不能在C++中实现它。您必须使用LOCK CMPXCHG。以下是我的答案:
; BL is the mutex id
; shared_val, a memory address
CMP [shared_val],BL ; Perhaps it is locked to us anyway
JZ .OutLoop2
.Loop1:
CMP [shared_val],0xFF ; Free
JZ .OutLoop1 ; Yes
pause ; equal to rep nop.
JMP .Loop1 ; Else, retry
.OutLoop1:
; Lock is free, grab it
MOV AL,0xFF
LOCK CMPXCHG [shared_val],BL
JNZ .Loop1 ; Write failed
.OutLoop2: ; Lock Acquired
- 从不同线程使用int64的不同字节安全吗
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 虚拟决赛作为安全
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何将元素添加到数组的线程安全函数?
- C++中的线程安全删除
- 通过网络、跨平台传递std::变体是否安全
- 在std::thread中,joinable()然后join()线程安全吗
- 使用std::istream::peek()总是安全的吗
- 从值小于256的uint16到uint8的Endian安全转换
- 在c++队列中使用pop和visit实现线程安全
- 在类型和包装器之间reinterpret_cast是否安全<Type>?
- 以线程安全的方式调用"QQuickPaintedItem::updateImage(const QImage&image)"(no QThread)
- 全局变量 多读取器 一个写入器多线程安全?
- 安全到标准:移动会员?
- AcquireCredentialsHandleA() 返回 PFX 文件的0x8009030e(安全包中没有可用的凭据
- 共享队列的线程安全
- boost::文件系统::recursive_directory_iterator多线程安全
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- 史上最简单的Mutex.这个例子行得通吗?它是线程安全的吗