web-dev-qa-db-ja.com

C ++グローバル変数を使用したマルチスレッドの理解

いくつかのグローバル変数を宣言するC++プログラムがあります。その後、いくつかのタスクを実行するためにいくつかのスレッドに分割されます。これらのスレッドは、これらのグローバル変数のいくつかを読み書きします。

2つのスレッドがreading同じ変数である場合、アプリのクラッシュは発生しますか?または、1つのスレッドwritesが別のスレッドが現在読み取っている場合にのみ、アプリクラッシュが発生しますか?

では、2番目の質問に対する答えが「はい」の場合、次のコードサンプルでこの問題を解決できますか?

#include <string>
#include <thread>
#include <mutex>
using namespace std;

mutex m;
string var = "foo";

// function to provide read and write access
// "protected" with mutex
string test(string value = "")
{
    m.lock();
    if (value == "")
    {
        m.unlock();
        return var;
    }
    else
    {
        var = value;
        m.unlock();
        return "";
    }
}

void thread1()
{
    // use global variable local
    string localVar = test();
}
void thread2()
{
    // overwrite global variable
    test("bar");
}
void thread3()
{
    // use global variable local
    string localVar = test();
}

int main()
{    
    thread t1(thread1);
    thread t2(thread2);
    thread t3(thread3);

    t1.join();
    t2.join();
    t3.join();

    return 0;
}

さらに:この部分は

// ...
if (value == "")
{
    m.unlock();
    return var;
}
// ...

スレッド保存も?

そして最後の質問です。私のプログラムは現在、1つのmutexのみを使用して、2つのスレッド(同じ関数!)が同時に実行されないようにしています。グローバル変数にミューテックスを使用していません。この「状況」により、例外コード0xc0000005でapp-crash(モジュール:「ntdll.dll」)が発生する可能性がありますか?

前もって感謝します!

10
SaschaP

複数読み取りは常にスレッドセーフです。 1つのスレッドが非アトミック変数varに書き込んでいる間、他のスレッドがvarから読み取りを行っているとすぐに、競合状態の危険があります。したがって、ほとんど問題はありませんが、次のようなミューテックスガードを使用します(これらはRAIIであるため、例外がなく安全でクリーンなC++です)。

#include <mutex>
#include <string>
// ...
std::mutex m;
std::string var = "foo";
// ...
std::string test(const std::string& value = "")
{
    std::lock_guard<std::mutex> lock(m);
    if (value == "")
    {
        return var;
    }
    else
    {
        var = value;
        return "";
    }
}
8
Paul Evans

2つのスレッドがreading同じ変数である場合、アプリのクラッシュは発生しますか?

いいえ。複数のスレッドからのみ読み取る場合は、常に安全です。

1つのスレッドwritesが別のスレッドが現在読み取っている場合にのみ、アプリクラッシュが発生しますか?

正確ではないが、それはcanがクラッシュにつながり、それがコードで起こっていることです。複数のスレッドから同時に読み書きするアプリケーションをクラッシュさせるという点では、「危険」ではありません。最悪のケースは、いくつかの場所で誤った値を取得することです。それ自体がアプリをクラッシュさせることはありませんが、最終的には確実にそれを引き起こす可能性があります。問題は、読み取っているデータの意味がプリミティブ値(整数など)以外の場合です。たとえば、メモリアドレス(ポインタ)を読み取っていて、そのアドレスのメモリにアクセスしようとしたが、メモリが既に解放されている場合は、問題が発生しています。これがコードで発生しています。文字列の文字が新しいアドレスに移動しましたが、古いアドレスを読み取ろうとしています。

問題を解決するには、操作全体をロック内にラップする必要があります。これには一時変数を使用できます。

string test(string value = "")
{
    m.lock();
    if (value == "")
    {
        string temp = var;
        m.unlock();
        return temp;
    }
    else
    {
        var = value;
        m.unlock();
        return "";
    }
}

正しい解決策は、ポールの答えです。

6
Amit

2つのスレッドがreadingが同じ変数である場合、アプリのクラッシュは発生しますか?

いいえ、グローバル変数を同時に安全に読み取ることはできません(同時に誰も書き込みを行わないことがわかっている場合)。読み取り操作ではグローバル値は変更されないため、変更されず、すべてのリーダーが同じ値を「認識」します。

または、別のスレッドが現在読み取っている変数に1つのスレッドwritesがある場合にのみ、app-crashが発生しますか?

一般的には、少なくとも読み取りと書き込みの同時処理のためではありません。クラッシュは、その副作用である可能性があります。たとえば、ポインタ値を更新すると同時に読み取る場合、ポインタが指すデータにアクセスしようとします。読み取った値が無効な場合、おそらくクラッシュします。

さらに:この部分は

// ...
if (value == "")
{
    m.unlock();
    return var;
}
// ...

スレッド保存も?

いいえ。ミューテックスmはローカル変数valueのみを保護します。これはローカルであるため、保護する必要はありません。ただし、別のスレッドがmutexに書き込みを行っている間に、mutexを解放してグローバルvar変数をコピー(read)します。スレッドセーフにするには、std::lock_guardそして、手動でmutexをロック/ロック解除する必要はありません。またはコードを次のように更新します。

m.lock();
if (value == "")
{
    string ret_val(var);
    m.unlock();
    return ret_val;
}

グローバル変数にミューテックスを使用していません。この「状況」がアプリのクラッシュを引き起こす可能性がありますか

前に書いたように、はい、副作用としてアプリがクラッシュする可能性があります。

1
Alex Lop.

簡単な質問、簡単な答え:

2つのスレッドが同じ変数を読み取っている場合、アプリがクラッシュしますか?

番号。

1つのスレッドが現在別のスレッドが読み取っている変数に書き込む場合にのみ、アプリクラッシュが発生しますか?

番号。

ただし、実際にロックやミューテックスなどを使用して、プログラムで期待される出力が得られるようにする必要があります。あるスレッドが別のスレッドによって読み取られている変数に書き込む場合、プログラム自体はクラッシュしませんが、読み取りスレッドが実際に読み取る値は何ですか?書き込まれる前の値または上書きされた後の値?

0
Lloyd Crawley

ミューテックスなしではスレッドセーフではありません

提案されたソリューションはまだ完全に正しくありません。

varはミューテックスの外部で読み取られ、そのときに変更される可能性があります。

これはC++ 11のように見えます。 std::stringは、C++ 11で禁止されている共有文字列を使用していたため、読み取りで問題が発生する可能性がありました。

読み取り+書き込みの場合、コピー時にポインターが変更されると、c0000005(アクセス違反)が発生する可能性があります。

0
mksteve