5

我想问一下最简单的多线程互斥方法。以下代码是线程安全的(快速n脏)吗?

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()));
}

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

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

谢谢你。

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

4

7 回答 7

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);

如果您有较旧的实现,那么您将不得不依赖任何可用的扩展/库,因为当时的语言或标准库没有任何帮助。

于 2013-02-11T19:00:02.820 回答
5

不,这不是线程安全的。之间有比赛

if(!locked)

this->locked = 1;

如果这两个语句之间有上下文切换,你的锁机制就会崩溃。您需要一个原子test and set指令,或者只是使用现有的mutex.

于 2013-02-11T18:59:52.700 回答
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;
}
于 2013-02-11T19:03:41.183 回答
2

这行得通吗?它是线程安全的吗?

不,它有时会失败。

仅当其他线程在执行这两行之间不做任何事情,您的互斥锁才会起作用:

if(!locked)
{
    this->locked = 1;

...而您并没有确保这一点。

要了解互斥锁的编写方式,请参阅SO 帖子

于 2013-02-11T18:58:41.807 回答
2

不,这不是线程安全的。

myclass::add考虑两个或多或少同时运行的线程。另外,假设 的.locked值为false

第一个线程执行到并包括这一行:

if(!locked)
{

现在想象系统将上下文切换到第二个线程。它也执行到同一行。

现在我们有两个不同的线程,都认为它们具有独占访问权,并且都在!lockedif 的条件内。

他们都会vals.push_back()或多或少同时打电话。

繁荣。

于 2013-02-11T18:59:50.083 回答
1

其他人已经展示了你的互斥锁是如何失败的,所以我不会重复他们的观点。我只会添加一件事:最简单的互斥锁实现比您的代码复杂得多。

如果您对细节感兴趣(或者即使您不感兴趣 - 这是每个软件开发人员都应该知道的内容),您应该查看Leslie Lamport 的烘焙算法并从那里开始。

于 2013-02-11T19:05:25.860 回答
0

您不能在 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
于 2019-01-02T09:09:25.627 回答