1

WinAPI のクリティカル セクションを同期メカニズムとして使用して、マルチスレッド コンソール アプリケーションを作成しています。5 つのスレッドを作成する必要があります。すべてのスレッドには、スレッドによって表示される独自の文字列があります。スレッドは、文字列を 1 つずつ順番に出力する必要があります。

だから、私はスレッド機能を持っています:

void thread_routine(void* data)
{
    std::string* st = (std::string*)data;
    while(1)
    {
        /* enter critical section */
        std::cout << st; // not exactly that way, but I hope that you understood what I've meant.
        Sleep(100);
        /* leave critical section */
    }
}

そして私の中でmain()

for(int i = 0; i < 10; ++i)
    _beginthread(thread_routine, 0, strings[i]);

[o1]のようなものが表示されることを期待していました。

1) First thread
2) Second thread
3) Third thread
4) Fourth thread
5) Fifth thread
... and so on

しかし、この出力の代わりに[o2]のようなものを見ました:

1) First thread
1) First thread
3) Third thread
2) Second thread
5) Fifth thread
... and so on

スレッドがクリティカル セクションを通過する順序は不明であるため、セクションはランダムにキャプチャされますが、スレッドを同期して[o1]のような出力を取得する必要があります。それで、私は何をしなければなりませんか?パターンはありますか?

4

6 に答える 6

3

クリティカル セクションは、この問題を解決する方法ではありません。クリティカル セクションは、相互排除デバイスにすぎません。特定の時間に 1 つのスレッドだけが特定のコードを実行できるようにすることを目的としています。シーケンスに使用することを意図したものではなく、そのタスクに特に適しているわけでもありません。問題は、クリティカル セクションを取得せずに待つことができないことです。

スレッド B が、スレッド A の作業を続行する前に終了するまで待機する必要がある場合を考えてみましょう。クリティカル セクションを使用する場合 (それを と呼びますcs1)、スレッド B がそれを取得しようとする前に、スレッド A がそれを取得することを確認する必要があります。つまり、スレッド B がクリティカル セクションを取得する前に、スレッド A が実行を開始してクリティカル セクションを取得することを 100% 確認する必要があります

一方、イベント オブジェクトを使用すると、イベントを待機したり、イベントが既に発生している場合は続行したりできます。したがって、スレッド B は、スレッド A が開始する前にイベントを待機できます。そして、スレッド A (または誰か) がイベントを設定するまで待機します。あなたが説明する問題 (スレッド B が何らかのイベントが発生するのを待つ必要がある) は、まさにイベント オブジェクトが解決するように設計されたタイプのものです。

3 つのスレッドが並行して処理され、ある時点で他のイベントが発生するまで待機する必要がある場合、イベントを作成して待機するコードを記述します。簡単に言うと:

HANDLE event1_done = CreateEvent(...);
HANDLE event2_done = CreateEvent(...);
HANDLE event3_done = CreateEvent(...);

すべてのイベントは手動でリセットされ、初期状態は通知されません。

メイン スレッドが 3 つのイベントを開始します。次に、3 つ目が終了するのを待ちます。

WaitForSingleObject(event3_done, INFINITE);

個々のスレッドは処理を行い、それぞれのイベントを待ちます。もちろん、スレッド 1 は待機しません。これを行うだけです:

// thread 1
// do processing
// then signal that it's finished
SetEvent(event1_done);

スレッド 2 はその処理を行い、 を待機してevent1_doneから、 を設定しますevent2_done

// thread 2
// do processing
// wait for the first thread to complete
WaitForSingleObject(event1_done, INFINITE);
// do whatever
// and then signal that it's done
SetEvent(event2_done);

スレッド 3 はスレッド 2 と同じです。イベント名のみ変更。

この方法とクリティカル セクションを使用する方法の違いは、複数のスレッドが同じイベント オブジェクトで待機している可能性があることです。そのイベントが設定されると、そのオブジェクトを待機しているすべてのスレッドが解放されます。それらの 1 つだけを解放したい場合は、自動リセット イベントを使用します。

また、これらのイベントを再利用したい場合は、それらをリセットする必要があることに注意してください ( を呼び出しますResetEvent)。それをいつ行うかはあなた次第です。

于 2013-10-31T21:07:32.747 に答える
2

独自のスケジューラを作成する必要があります。指定された順序でスレッドを起動する別のスレッド。その場合、待機可能なオブジェクト (セマフォなど) を含む、より複雑なデータをスレッドに渡す必要があります。私は WinAPI の経験がありません。これは単なるアイデアです。

void scheduler_thread(void* data) {
  scheduler_data* threads = (scheduler_data*)data;
  int index = 0;
  while (true) {
    notify_waitable_object(threads->waitable_object[index]);
    sleep(timeout);
    index = (index + 1) % threads->thread_count;
  }
}

void thread_procedure(void* data) {
  some_thread_data* p = (some_thread_data*)data;
  while(true)
  {
    wait_for_notify(p->wait_object);
    std::cout << p->st;
  }  
}
于 2013-10-31T20:52:28.867 に答える
1

おそらく設計上の問題があり、それを修正する必要があります!

しかし、本当にスレッドを同期したいのであれば、ここに 1 つのアプローチがあります。これは非常に非効率的であり、スレッドのいずれかが重要な部分をスキップした場合 (try-catch など) にデッドロックが発生するため、おそらく反対票を投じられますが、それでもアプローチは次のとおりです。

#include <thread>
#include <atomic>
#include <cstdlib>
#include <iostream>
#include <vector>

void myFunc(int idx,std::atomic<int> * counter){

    std::this_thread::sleep_for(std::chrono::milliseconds(std::rand()%100));
    // Don't know about memory_order stuff
    // If you want to use this approach, you should read about it.
    while(counter->load()!=idx){
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    std::cout << idx << " thread here\n";
    counter->store(idx+1);
}

int main(){
    std::srand(std::time(0));
    std::atomic<int> counter(0);
    std::vector<std::thread> thread_vector;
    for(int i=0;i<10;i++)
        thread_vector.push_back(std::thread(myFunc,i,&counter));

    for(int i=0;i<10;i++)
        thread_vector[i].join();
}
于 2013-10-31T20:40:53.753 に答える