我想问一下有关多线程的最简单的Mutex方法。以下代码是否是线程安全的(快速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()));
}
这有用吗?它是线程安全的吗?我只想了解如何编写最简单的互斥锁。
如果你对我的例子有任何建议,那就太好了。
谢谢。
感谢您说这不起作用。你能建议一个独立于编译器的修复程序吗?
答案 0 :(得分:8)
它是线程安全的吗?
当然不是。如果在检查和设置锁之间抢占线程,则第二个线程可以获取该锁;如果控制然后返回到第一个线程,那么两者都将获得它。 (当然,在现代处理器上,两个或多个内核可以同时执行相同的指令,以获得更多的欢乐。)
至少,你需要一个原子测试和设置操作来实现这样的锁。 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);
如果您有较旧的实现,那么您将不得不依赖于您可以使用的任何扩展/库,因为当时语言或标准库没有任何帮助。
答案 1 :(得分:5)
不,这不是线程安全的。
之间有一场比赛if(!locked)
和
this->locked = 1;
如果这两个语句之间存在上下文切换,则锁定机制会崩溃。您需要原子test and set
指令,或者只需使用现有的mutex
。
答案 2 :(得分:5)
此代码不提供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;
}
答案 3 :(得分:2)
这有用吗?它是线程安全的吗?
没有。它有时会失败。
只有当其他线程从不在执行这两行之间执行任何操作时,您的互斥锁才会起作用:
if(!locked)
{
this->locked = 1;
......你还没确定。
要了解互斥锁写作的 ,请参阅this SO post。
答案 4 :(得分:2)
不,这不是线程安全的。
考虑两个线程在或多或少同时运行myclass::add
。另外,假设.locked
的值为false
。
第一个线程执行直到并包括这一行:
if(!locked)
{
现在假设系统将上下文切换到第二个线程。它也可以执行同一行。
现在我们有两个不同的主题,都认为他们有独占访问权限,并且都在if的!locked
条件内。
他们将同时或多或少地呼叫vals.push_back()
。
动臂。
答案 5 :(得分:1)
其他人已经展示了你的互斥锁如何失败,所以我不会重复他们的观点。我只会添加一件事:最简单的互斥实现比你的代码复杂得多。
如果你对这个细节很感兴趣(或者即使你不是 - 这是每个软件开发人员应该知道的东西),你应该看看Leslie Lamport's Bakery Algorithm并从那里开始。
答案 6 :(得分:0)
您不能在C ++中实现它。您必须使用LOCK CMPXCHG。这是我对here的回答:
; 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