最简单的Mutex。这个例子有用吗?它是线程安全的吗?

时间:2013-02-11 18:55:19

标签: c++ multithreading parallel-processing global-variables mutex

我想问一下有关多线程的最简单的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()));
}

这有用吗?它是线程安全的吗?我只想了解如何编写最简单的互斥锁。

如果你对我的例子有任何建议,那就太好了。

谢谢。

感谢您说这不起作用。你能建议一个独立于编译器的修复程序吗?

7 个答案:

答案 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