5

マルチスレッドのためのこれまでで最も単純な 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()));
}

これは機能しますか?スレッドセーフですか?最も単純なミューテックスを作成する方法を理解しようとしています。

私の例について何かアドバイスがあれば、よろしくお願いします。

ありがとうございました。

うまくいかないと言ってくれてありがとう。コンパイラに依存しない修正を提案できますか?

4

7 に答える 7

8

スレッドセーフですか?

確かにそうではありません。ロックのチェックと設定の間にスレッドが横取りされると、2 番目のスレッドがそのロックを取得する可能性があります。その後、制御が最初のスレッドに戻ると、両方がそれを取得します。(そしてもちろん、最新のプロセッサでは、2 つ以上のコアが同じ命令を同時に実行することで、さらに面白くすることができます。)

少なくとも、このようなロックを実装するには、アトミックなテストと設定の操作が必要です。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;

これら 2 つのステートメントの間にコンテキストの切り替えがあると、ロック メカニズムが機能しなくなります。アトミック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

これは機能しますか?スレッドセーフですか?

いいえ。失敗することもあります。

ミューテックスは、他のスレッドがこれら2行の実行の間に何もしない場合にのみ機能します。

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

...そしてあなたはそれを保証していません。

ミューテックスの記述方法については、このSOの投稿を参照してください。

于 2013-02-11T18:58:41.807 に答える
2

いいえ、それはスレッドセーフではありません。

2つのスレッドmyclass::addがほぼ同時に実行されていると考えてください。.lockedまた、の値がであると想像してくださいfalse

最初のスレッドは、次の行まで実行されます。

if(!locked)
{

ここで、システムがコンテキストを2番目のスレッドに切り替えると想像してください。また、同じ行まで実行されます。

これで、2つの異なるスレッドができました。どちらも排他的アクセス権があると信じており、どちらも!lockedifの条件の範囲内にあります。

彼らは両方ともvals.push_back()多かれ少なかれ同時に電話をかけます。

ブーム。

于 2013-02-11T18:59:50.083 に答える
1

ミューテックスが失敗する可能性があることを他の人がすでに示しているので、それらのポイントを再ハッシュすることはしません。1 つだけ追加します。最も単純なミューテックスの実装は、コードよりもはるかに複雑です。

核心に興味がある場合 (またはそうでない場合でも、これはすべてのソフトウェア開発者が知っておくべきことです)、Leslie Lamport の Bakery Algorithmを見て、そこから進んでください。

于 2013-02-11T19:05:25.860 に答える