none
マルチスレッドの同期におけるseteventの誤動作 RRS feed

  • 質問

  • 音の信号処理を行うためのマルチスレッドプログラムを作っているのですが、同期のプログラムがうまくいかないため、ご教示いただきたくポスト致します。仕様は次の通りです。
    1. bg_thread, main_thread, sub_thread[0], sub_thread[1]の4つのスレッドがある。
    2. bg_threadは一定の周期毎にmain_threadをトリガーする
    3. main_threadはsub_thread[0]を毎回トリガーし、sub_thread[1]を1回毎にトリガーする。
    4. main_threadはmain_class内の関数
    5. sub_thread[0], sub_thread[1]はそれぞれsub_class内の関数
    プログラムは以下のとおりです。

    #include "process.h" #include "windows.h" #include "stdio.h" class Sub_class { public: HANDLE hEvent2; Sub_class(int no); bool loop_ok; bool calcstart; int sub_class_no; void do_sub_loop2(); }; class Main_class { public: bool thread_go; bool go_flag = false; Sub_class *sub_cls[2]; Main_class(); ~Main_class(); int start_loop(); }; Main_class *main_cls = 0; HANDLE g_bg_wait = 0; HANDLE g_main_event = 0; bool bg_go = true; unsigned __stdcall start_bg_loop(void *parg) { WaitForSingleObject(g_bg_wait, INFINITE); while (bg_go) { Sleep(100); // goto sleep for 100 milliseconds SetEvent(g_main_event); } return 0; } unsigned __stdcall start_main_loop(void *parg) { main_cls->start_loop(); return 0; } //--------------------------------------------------------------------------------- int main() { g_bg_wait = CreateEvent(NULL, TRUE, FALSE, "bg_event"); g_main_event = CreateEvent(NULL, FALSE, FALSE, "main_event");

    _beginthreadex(0, 0, &start_bg_loop, 0, 0, 0); _beginthreadex(0, 0, &start_main_loop, 0, 0, 0); main_cls = new Main_class(); main_cls->sub_cls[0] = new Sub_class(0); main_cls->sub_cls[1] = new Sub_class(1); Sleep(1000); bg_go = false; CloseHandle(g_bg_wait); CloseHandle(g_main_event); } int Main_class::start_loop() { Sleep(10); SetEvent(g_bg_wait); while (thread_go) { WaitForSingleObject(g_main_event, INFINITE); printf("Trigger SubClass 0 : %d\n", (int)sub_cls[0]->hEvent2); sub_cls[0]->calcstart = true; SetEvent(sub_cls[0]->hEvent2); if (go_flag) { printf("Trigger SubClass 1 : %d\n", (int)sub_cls[1]->hEvent2); sub_cls[1]->calcstart = true; SetEvent(sub_cls[1]->hEvent2); } else { sub_cls[1]->calcstart = false; } go_flag = !go_flag; } return 0; } Main_class::Main_class() { thread_go = true; } Main_class::~Main_class() { thread_go = false; } static unsigned __stdcall executeLauncher2(void* args) { reinterpret_cast<Sub_class*>(args)->do_sub_loop2(); return 0; } Sub_class::Sub_class(int no) { sub_class_no = no; loop_ok = true; hEvent2 = CreateEvent(0, FALSE, FALSE, "event_2"); _beginthreadex(0, 0, &executeLauncher2, (void *)this, 0, 0); } void Sub_class::do_sub_loop2() { while (loop_ok) { WaitForSingleObject(hEvent2, INFINITE); if (calcstart) printf("Start SubClass %d : %d : OK\n", sub_class_no, (int)hEvent2); else printf("Start SubClass %d : %d : NG ---\n", sub_class_no, (int)hEvent2); } }

    このプログラムを動かすと次のような出力が得られます。

    Trigger SubClass 0 : 212
    Start SubClass 0 : 212 : OK
    Trigger SubClass 0 : 212
    Trigger SubClass 1 : 220
    Start SubClass 1 : 220 : NG ---
    Start SubClass 0 : 212 : OK
    Trigger SubClass 0 : 212
    Start SubClass 1 : 220 : NG ---
    Trigger SubClass 0 : 212
    Trigger SubClass 1 : 220
    Start SubClass 0 : 212 : OK
    Start SubClass 1 : 220 : OK
    Trigger SubClass 0 : 212
    Start SubClass 0 : 212 : OK
    Trigger SubClass 0 : 212
    Trigger SubClass 1 : 220
    Start SubClass 1 : 220 : NG ---
    Start SubClass 0 : 212 : OK
    Trigger SubClass 0 : 212
    Start SubClass 1 : 220 : NG ---
    Trigger SubClass 0 : 212
    Trigger SubClass 1 : 220
    Start SubClass 0 : 212 : OK
    Start SubClass 1 : 220 : OK
    Trigger SubClass 0 : 212
    Start SubClass 0 : 212 : OK


    結果からわかるとおり、sub_thread[0]をトリガー(SetEvent 212)しているのに、sub_thread[1]が起動すること(WaitForSingleObject 220の次にNGがプリントされる)がたびたびあります。sub_thread[0]をトリガーしたときはsub_thread[0]が起動し、
    sub_thread[1]をトリガーしたときはsub_thread[1]が起動するように、すなわちNG行が出ないようにするためには、どのようにすればよいでしょうか?



    • 編集済み S.Ise 2018年3月15日 7:48
    2018年3月15日 7:16

回答

  • CreateEvent(0, FALSE, FALSE, "event_2") で名前付きイベントを作成しています。名前が付けられていると同名のイベントは共有されます。

    つまり、SetEvent(sub_cls[0]->hEvent2) を行うだけで sub_cls[0]->do_sub_loop2 と sub_cls[1]->do_sub_loop2 との両方に通知されてしまいます。CreateEventではイベント名に NULL を指定することで匿名イベントが作成できます。この場合、個々のイベントは全て独立しますので、共有する必要がないのであれば匿名イベントをお勧めします。

    2018年3月15日 7:47

すべての返信

  • CreateEvent(0, FALSE, FALSE, "event_2") で名前付きイベントを作成しています。名前が付けられていると同名のイベントは共有されます。

    つまり、SetEvent(sub_cls[0]->hEvent2) を行うだけで sub_cls[0]->do_sub_loop2 と sub_cls[1]->do_sub_loop2 との両方に通知されてしまいます。CreateEventではイベント名に NULL を指定することで匿名イベントが作成できます。この場合、個々のイベントは全て独立しますので、共有する必要がないのであれば匿名イベントをお勧めします。

    2018年3月15日 7:47
  • ありがとうございました。解決しました。助かります。
    2018年3月15日 9:26
  • S.Ise さん、こんにちは。フォーラム オペレーターの立花です。
    MSDN フォーラムへご投稿くださいましてありがとうございます。

    フォーラム オペレーターからのお願いです。

    佐祐理さんから頂いた返信はお役に立ちましたでしょうか。
    同じ問題で後から閲覧した方がすぐに情報を見つけられるように、
    参考になった投稿には [回答としてマーク] のご設定くださいませ。

    ご協力の程、どうぞよろしくお願いいたします。

    フォーラム利用時の注意点のお知らせです。
    ※初めてご投稿された方を対象にお知らせしています。

    ご利用の際は、下記内容をお守りいただきますと
    情報が寄せられやすくなりますので、ぜひご一読ください。
    フォーラムでご質問頂くにあたっての注意点
    フォーラムのご利用方法(質問の投稿)について
    フォーラムへの回答に関するガイドラインおよびフォーラム運営について(再掲)
    ご意見、ご要望はこちらのフォーラムまで。
    各種設定方法はフォーラム内を [かんたん フォーラム ガイド] で検索してみてください。


    参考になった投稿には回答としてマークの設定にご協力ください
    MSDN/TechNet Community Support 立花楓

    2018年3月16日 0:08
    モデレータ