web-dev-qa-db-ja.com

C ++スレッド間のメモリ共有

私はC++でのスレッド処理に不慣れで、メモリがスレッド間でどのように共有/共有されないかを明確に把握しようとしています。 C++ 11でstd::threadを使用しています。他のSOの質問で読んだことから、stackメモリは1つのスレッドのみが所有し、heapメモリはスレッド間で共有されます。スタックとヒープについて私が理解していることから、次のことが当てはまるはずです。

#include <thread>
using namespace std;

class Obj {
public:
    int x;
    Obj(){x = 0;}
};

int main() {
    Obj stackObj;
    Obj *heapObj = new Obj();
    thread t([&]{
        stackObj.x++;
        heapObj->x++;
    });
    t.join();
    assert(heapObj->x == 1);
    assert(stackObj.x == 0);
}

私がたくさんのものを台無しにした場合、許してください、ラムダ構文は私にとって非常に新しいものです。しかし、うまくいけば、私がやろうとしていることが首尾一貫しています。これは期待どおりに機能しますか?そうでない場合、私は何を誤解していますか?

20
Michael Dorst

メモリはメモリです。 C++のオブジェクトは、メモリ内のいくつかの場所を占有します。その場所はスタックまたはヒープ上にあるか、静的に割り当てられている可能性があります。オブジェクトがどこにあるかは関係ありません。オブジェクトへの参照またはポインタを持つスレッドは、オブジェクトにアクセスできます。 2つのスレッドがオブジェクトへの参照またはポインタを持っている場合、両方のスレッドがオブジェクトにアクセスできます。

プログラムでは、提供するラムダ式を実行するワーカースレッドを(_std::thread_を作成して)作成します。 stackObjheapObjの両方を参照でキャプチャするため(_[&]_キャプチャのデフォルトを使用)、そのラムダにはこれらのオブジェクトの両方への参照があります。

これらのオブジェクトは両方ともメインスレッドのスタックにあります(heapObjは、メインスレッドのスタックにあり、ヒープにある動的に割り当てられたオブジェクトを指すポインター型オブジェクトです)。これらのオブジェクトのコピーは作成されません。むしろ、ラムダ式にはオブジェクトへの参照があります。 stackObjを直接変更し、heapObjが指すオブジェクトを間接的に変更します。

メインスレッドがワーカースレッドに参加した後、_heapObj->x_と_stackObj.x_の両方の値は_1_になります。


値キャプチャのデフォルト(_[=]_)を使用した場合、ラムダ式にはcopiedstackObjheapObjの両方が含まれます。ラムダ式の式_stackObj.x++_はcopyをインクリメントし、main()で宣言したstackObjは変更されません。

heapObjを値でキャプチャすると、ポインター自体のみがコピーされるため、ポインターのコピーが使用されている間も、動的に割り当てられた同じオブジェクトを指します。式_heapObj->x++_は、そのポインターを逆参照し、new Obj()を介して作成したObjを生成し、その値をインクリメントします。次に、main()の最後に、_heapObj->x_が増加したことを確認します。

(値によってキャプチャされたオブジェクトを変更するには、ラムダ式をmutableと宣言する必要があります。)

26
James McNellis

heapObj->xおよびstackObj.x1になることについて、ジェームズマクネリスに同意します。

さらに、スレッドを生成した直後にjoinを実行するため、このコードonlyは機能します。スレッドを開始し、実行中にさらに作業を行った場合、例外によってスタックが巻き戻され、新しいスレッドのstackObjが突然無効になります。技術的に可能であっても、スレッド間でスタックメモリを共有することは悪い考えです。

1
japreiss