2 つの連続する式の間、関数の呼び出しとその本体の最初の式の実行の間、またはコンストラクターの呼び出しとその初期化子の実行の間で何が起こるかについて、私はめったに考えたことがありません。それから私は並行性について読み始めました...
1.)本体が同じオブジェクトの初期化でstd::thread始まる、同じ callable (関数、ファンクター、ラムダなど) を持つ のコンストラクターへの 2 つの連続した呼び出しで、最初のコンストラクター呼び出しに対応するスレッドがロックを実行することを標準で保証しますか? -最初に保護されたコード?std::lock_guardstd::mutexthread
2.) 標準が保証しない場合、2 番目のthreadコンストラクター呼び出しに対応するスレッドが最初に保護されたコードを実行する可能性は理論的または実際的にありますか? thread(たとえば、最初のコンストラクター呼び出しのイニシャライザーまたは本体の実行中の重いシステム負荷)
これは、グローバルstd::mutexオブジェクトmと にunsigned num初期化されたグローバル1です。foofunctionの本体の左中括弧{と. の間には空白しかありませんstd::lock_guard。には、 と のmain2 つがstd::threadありt1ますt2。t1最初にスレッド コンストラクターを呼び出します。t2スレッド コンストラクタを 2 番目に呼び出します。各スレッドは へのポインタで構築されますfoo。引数でt1呼び出します。引数で呼び出します。どちらのスレッドが最初にロックするかに応じて、両方のスレッドがロック保護されたコードを実行した後、 の値は aまたは aになります。等しくなりますfoounsigned1t2foounsigned2mutexnum43num4ロックに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ことを意味しますか? mutexcallable の本体内に、初期化の行の前に callable に渡されたパラメーターに依存するコードが存在しない場合はstd::lock_guard? static(また、特定の呼び出しを意図的に遅らせるために使用できる、呼び出された回数のカウンターなど、呼び出し可能オブジェクトのローカル変数も除外します。)