私は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);
}
私がたくさんのものを台無しにした場合、許してください、ラムダ構文は私にとって非常に新しいものです。しかし、うまくいけば、私がやろうとしていることが首尾一貫しています。これは期待どおりに機能しますか?そうでない場合、私は何を誤解していますか?
メモリはメモリです。 C++のオブジェクトは、メモリ内のいくつかの場所を占有します。その場所はスタックまたはヒープ上にあるか、静的に割り当てられている可能性があります。オブジェクトがどこにあるかは関係ありません。オブジェクトへの参照またはポインタを持つスレッドは、オブジェクトにアクセスできます。 2つのスレッドがオブジェクトへの参照またはポインタを持っている場合、両方のスレッドがオブジェクトにアクセスできます。
プログラムでは、提供するラムダ式を実行するワーカースレッドを(_std::thread
_を作成して)作成します。 stackObj
とheapObj
の両方を参照でキャプチャするため(_[&]
_キャプチャのデフォルトを使用)、そのラムダにはこれらのオブジェクトの両方への参照があります。
これらのオブジェクトは両方ともメインスレッドのスタックにあります(heapObj
は、メインスレッドのスタックにあり、ヒープにある動的に割り当てられたオブジェクトを指すポインター型オブジェクトです)。これらのオブジェクトのコピーは作成されません。むしろ、ラムダ式にはオブジェクトへの参照があります。 stackObj
を直接変更し、heapObj
が指すオブジェクトを間接的に変更します。
メインスレッドがワーカースレッドに参加した後、_heapObj->x
_と_stackObj.x
_の両方の値は_1
_になります。
値キャプチャのデフォルト(_[=]
_)を使用した場合、ラムダ式にはcopiedstackObj
とheapObj
の両方が含まれます。ラムダ式の式_stackObj.x++
_はcopyをインクリメントし、main()
で宣言したstackObj
は変更されません。
heapObj
を値でキャプチャすると、ポインター自体のみがコピーされるため、ポインターのコピーが使用されている間も、動的に割り当てられた同じオブジェクトを指します。式_heapObj->x++
_は、そのポインターを逆参照し、new Obj()
を介して作成したObj
を生成し、その値をインクリメントします。次に、main()
の最後に、_heapObj->x
_が増加したことを確認します。
(値によってキャプチャされたオブジェクトを変更するには、ラムダ式をmutable
と宣言する必要があります。)
heapObj->x
およびstackObj.x
が1
になることについて、ジェームズマクネリスに同意します。
さらに、スレッドを生成した直後にjoin
を実行するため、このコードonlyは機能します。スレッドを開始し、実行中にさらに作業を行った場合、例外によってスタックが巻き戻され、新しいスレッドのstackObj
が突然無効になります。技術的に可能であっても、スレッド間でスタックメモリを共有することは悪い考えです。