10

以下は安全ですか?

私はスレッド化が初めてで、時間のかかるプロセスを C++ プログラムの別のスレッドに委譲したいと考えています。ブースト ライブラリを使用して、次のようなコードを記述しました。

thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag);

finished_flag は私のクラスのブール値のメンバーです。スレッドが終了すると、値が設定され、プログラムのメイン ループがその値の変更をチェックします。私は 1 つのスレッドしか開始しないため、これで問題ないと思います。値を変更するのはそのスレッドだけです (スレッドを開始する前に初期化される場合を除く)。ロックやミューテックスなどを使用する必要がある

4

5 に答える 5

11

finished_flagのタイプについて言及したことはありません...

それが単純なboolであれば、うまくいくかもしれませんが、いくつかの理由から、それは確かに悪い習慣です。まず、一部のコンパイラは、 finished_flag変数の読み取りをキャッシュします。これは、コンパイラが別のスレッドによって書き込まれているという事実を常に認識できるとは限らないためです。bool volatileを宣言することでこれを回避できますが、それは間違った方向に進んでいます。読み取りと書き込みが期待どおりに行われている場合でも、OS スケジューラが読み取り/書き込みの途中で 2 つのスレッドをインターリーブするのを止めるものは何もありません。別々のスレッドで 1 つの読み取り操作と 1 つの書き込み操作を行う場合、これはそれほど問題ではないかもしれませんが、続行するつもりで開始することをお勧めします。

一方、MFC の CEvent (またはboost と同等) のようなスレッドセーフなタイプの場合は、問題ありません。これが最良のアプローチです。単純なフラグであっても、スレッド間通信にはスレッドセーフな同期オブジェクトを使用します。

于 2008-08-29T08:10:55.983 に答える
7

メンバー変数を使用してスレッドが完了したことを知らせる代わりに、condition? あなたは既にブースト ライブラリを使用しておりcondition、スレッド ライブラリの一部です。

それをチェックしください。これにより、ワーカー スレッドは終了したことを「通知」することができ、メイン スレッドは実行中に状態が通知されたかどうかを確認し、完了した作業に対して必要なことを何でも行うことができます。リンクに例があります。

一般的なケースとして、リソースはスレッドによってのみ変更されると仮定する必要はありません。あなたはそれが何のためにあるのか知っているかもしれませんが、他の誰かが知らないかもしれません - メインスレッドが作業が完了したと考えて、正しくないデータにアクセスしようとするので、悲しみの終わりはありません! ワーカー スレッドがまだ使用している間に削除され、アプリがクラッシュする可能性もあります。a を使用するconditionと、これが役立ちます。

threadドキュメントを見るとthread.timed_join、メインスレッドで呼び出すこともできます。timed_joinスレッドが「結合」するまで、指定された量だけ待機します (結合とは、スレッドが終了したことを意味します)

于 2008-08-29T08:20:22.433 に答える
5

共有メモリを介したスレッド間の通信の詳細を本当に知りたい場合は、コンパイラが適切なアクセス セマンティクスを使用して古いバージョンのデータを取得しないことを保証したとしても、変数 volatile を宣言するだけでは十分ではありません。フラグを確認した後。CPU は、順不同で読み取りと書き込みを発行できます (x86 では通常発行されませんが、PPC では確実に発行されます)。C++9x には、コンパイラがコードを生成してメモリ アクセスを適切に順序付けることができるものはありません。

Herb Sutter の「Effective Concurrency」シリーズでは、C++ の世界がマルチコア/マルチプロセッサの世界とどのように交差するかについて、非常に詳細に考察しています。

于 2008-08-29T23:16:26.683 に答える
5

推定するつもりはありませんが、finished_flag変数の目的は、スレッドthrdが完了するまでメインスレッドを (ある時点で) 一時停止することのようです。

これを行う最も簡単な方法は、boost::thread::joinを使用することです

// launch the thread...
thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag);

// ... do other things maybe ... 

// wait for the thread to complete
thrd.join();
于 2008-09-21T18:47:59.027 に答える
2

スレッドが終了する前にフラグを設定する (またはイベントを通知する) ことは、競合状態です。スレッドはまだ OS に戻っているとは限らず、まだ実行中の可能性があります。

たとえば、動的ライブラリ (疑似コード) をロードするプログラムを考えてみます。

lib = loadLibrary("someLibrary");
fun = getFunction("someFunction");
fun();
unloadLibrary(lib);

そして、このライブラリがあなたのスレッドを使用しているとしましょう:

void someFunction() {
    volatile bool finished_flag = false;
    thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag);
    while(!finished_flag) { // ignore the polling loop, it's besides the point
        sleep();
    }
    delete thrd;
}

void myclass::mymethod() {
    // do stuff
    finished_flag = true;
}

myclass::mymethod()設定finished_flagするとtruemyclass::mymethod()まだ戻っていません。少なくとも、何らかの「リターン」命令を実行する必要があります (それ以上ではないにしても、デストラクタ、例外ハンドラ管理など)。実行中のスレッドmyclass::mymethod()がその時点より前にプリエンプトされるsomeFunction()と、呼び出しプログラムに戻り、呼び出しプログラムはライブラリをアンロードします。実行中のスレッドmyclass::mymethod()が再度実行されるようにスケジュールされると、「return」命令を含むアドレスが無効になり、プログラムがクラッシュします。

解決策は、戻る前someFunction()に電話することです。thrd->join()これにより、スレッドが OS に戻り、実行されなくなります。

于 2008-09-16T05:19:54.760 に答える