2 つの連続する式の間、関数の呼び出しとその本体の最初の式の実行の間、またはコンストラクターの呼び出しとその初期化子の実行の間で何が起こるかについて、私はめったに考えたことがありません。それから私は並行性について読み始めました...
1.)本体が同じオブジェクトの初期化でstd::thread
始まる、同じ callable (関数、ファンクター、ラムダなど) を持つ のコンストラクターへの 2 つの連続した呼び出しで、最初のコンストラクター呼び出しに対応するスレッドがロックを実行することを標準で保証しますか? -最初に保護されたコード?std::lock_guard
std::mutex
thread
2.) 標準が保証しない場合、2 番目のthread
コンストラクター呼び出しに対応するスレッドが最初に保護されたコードを実行する可能性は理論的または実際的にありますか? thread
(たとえば、最初のコンストラクター呼び出しのイニシャライザーまたは本体の実行中の重いシステム負荷)
これは、グローバルstd::mutex
オブジェクトm
と にunsigned
num
初期化されたグローバル1
です。foo
functionの本体の左中括弧{
と. の間には空白しかありませんstd::lock_guard
。には、 と のmain
2 つがstd::thread
ありt1
ますt2
。t1
最初にスレッド コンストラクターを呼び出します。t2
スレッド コンストラクタを 2 番目に呼び出します。各スレッドは へのポインタで構築されますfoo
。引数でt1
呼び出します。引数で呼び出します。どちらのスレッドが最初にロックするかに応じて、両方のスレッドがロック保護されたコードを実行した後、 の値は aまたは aになります。等しくなりますfoo
unsigned
1
t2
foo
unsigned
2
mutex
num
4
3
num
4
ロックにt1
打ち込む場合。t2
それ以外の場合は、num
に等しくなり3
ます。ループして各ループの最後にリセットすることで、num
これを 100,000 回試行しました。1
(私の知る限り、結果はどのスレッドがjoin()
最初に編集されるかに依存しませんし、依存すべきではありません。)
#include <thread>
#include <mutex>
#include <iostream>
std::mutex m;
unsigned short num = 1;
void foo(unsigned short par) {
std::lock_guard<std::mutex> guard(m);
if (1 == num)
num += par;
else
num *= par;
}
int main() {
unsigned count = 0;
for (unsigned i = 0; i < 100000; ++i) {
std::thread t1(foo, 1);
std::thread t2(foo, 2);
t1.join();
t2.join();
if (4 == num) {
++count;
}
num = 1;
}
std::cout << count << std::endl;
}
最終的には にcount
等しい100000
ので、t1
毎回レースに勝つことがわかります。しかし、これらの試験は何も証明しません。
3.) 「thread
コンストラクターを最初に呼び出す」という標準の命令は、常に「コンストラクターに渡された呼び出し可能オブジェクトを最初に呼び出す」ことを意味しthread
ますか?
4.) 「コンストラクターに渡された callable を最初に呼び出す」という標準の命令は、常に「最初にロックする」thread
ことを意味しますか? mutex
callable の本体内に、初期化の行の前に callable に渡されたパラメーターに依存するコードが存在しない場合はstd::lock_guard
? static
(また、特定の呼び出しを意図的に遅らせるために使用できる、呼び出された回数のカウンターなど、呼び出し可能オブジェクトのローカル変数も除外します。)