1

プログラムがアイドル状態のときに、バックグラウンドで複数のファイルを処理する複数のスレッドがあります。
ディスクのスループットを向上させるために、クリティカル セクションを使用して、2 つのスレッドが同じディスクを同時に使用しないようにしています。

(疑似) コードは次のようになります。

void RunThread(HANDLE fileHandle)
{
    // Acquire CRITICAL_SECTION for disk
    CritSecLock diskLock(GetDiskLock(fileHandle));

    for (...)
    {
        // Do some processing on file
    }
}

ユーザーがファイルの処理を要求したら、要求されたファイルを処理しているスレッドを除くすべてのスレッドを停止する必要があります。ファイルが処理されたら、すべてのスレッドを再開したいと思います。

それが悪い考えであるという事実を考えるとSuspendThread、関連する入力を処理しているスレッドを除くすべてのスレッドを停止するにはどうすればよいですか?

ミューテックス、セマフォ、イベントなど、どのような種類のスレッド オブジェクト/機能が必要ですか? そして、それらをどのように使用しますか?(Windows XP との互換性を期待しています。)

4

6 に答える 6

5

まったく違う方法で行くことをお勧めします。本当にすべてのディスクに 1 つのスレッドだけが必要な場合 (これが良い考えだとは思いません)、ディスクごとに 1 つのスレッドを作成し、ファイルを処理のためにキューに入れるときにファイルを分散する必要があります。

特定のファイルに対する優先リクエストを実装するには、通常の処理中 (そしてもちろんメイン キューの待機ループ) のいくつかの時点でスレッドに「優先スロット」をチェックさせます。

于 2012-12-14T10:59:49.327 に答える
1

ここでの問題は、優先度そのものではありません。スレッドが保持しているロックを元に戻し、別のスレッドがロックを取得できるようにする必要があるという事実です。「優先度」は、一連の実行可能なスレッドの実行をスケジュールする必要があることに関連します。実行できないスレッドを実行可能にしたい (別のスレッドが保持するロックを待機しているため)。

したがって、実装したい(あなたが言ったように):

if (ThisThreadNeedsToSuspend()) { ReleaseDiskLock(); WaitForResume(); ReacquireDiskLock(); }

あなたは(賢明に)スコープ付きロックを使用しているので、ロジックを逆にしたいと思います:

while (file_is_not_finished) {
    WaitUntilThisThreadCanContinue();
    CritSecLock diskLock(blah);
    process_part_of_the_file();
}
ReleasePriority();

...

void WaitUntilThisThreadCanContinue() {
    MutexLock lock(thread_priority_mutex);
    while (thread_with_priority != NOTHREAD and thread_with_priority != thisthread) {
        condition_variable_wait(thread_priority_condvar);
    }
}

void GiveAThreadThePriority(threadid) {
    MutexLock lock(thread_priority_mutex);
    thread_with_priority = threadid;
    condition_variable_broadcast(thread_priority_condvar);
}

void ReleasePriority() {
    MutexLock lock(thread_priority_mutex);
    if (thread_with_priority == thisthread) {
        thread_with_priority = NOTHREAD;
        condition_variable_broadcast(thread_priority_condvar);
    }
}

条件変数について調べてください。最近のすべての OS には条件変数があり、同様の基本操作があります。Boost と C++11 にもあります。

関数を書くprocess_part_of_the_fileことができない場合、このように構造化することはできません。代わりに、ディスクロックを解放して回復できるスコープ付きロックが必要です。これを行う最も簡単な方法は、それをミューテックスにすることです。そうすれば、同じミューテックスを使用して condvar を待つことができます。ミューテックス/condvar ペアとthread_with_priorityオブジェクトは、ほぼ同じ方法で引き続き使用できます。

優先度の変更に対するシステムの応答性に応じて、「ファイルの一部」のサイズを選択します。非常に応答性が必要な場合、スキームは実際には機能しません-これは協調マルチタスクです.

私はこの答えに完全に満足しているわけではありません.同じディスクロックですでに待機している他のスレッドがたくさんある場合、優先度のあるスレッドは長時間枯渇する可能性があります. それを避けるために、私はもっと考えました。おそらくディスクごとのロックではなく、条件変数とそれに関連するミューテックスの下ですべてを処理する必要があります。ただし、これで開始できることを願っています。

于 2012-12-14T11:31:03.613 に答える
1

スレッドに正常に停止するように依頼することができます。スレッド内のループでいくつかの変数をチェックし、その値に応じて作業を続行または終了します。

それについてのいくつかの考え:

  • この値の設定と確認は、クリティカル セクション内で行う必要があります。
  • クリティカル セクションはスレッドの速度を低下させるため、クリティカル セクションの取得と解放によってスレッドが停止しないように、必要に応じてスレッドを迅速に停止できる程度の頻度でチェックを行う必要があります。
于 2012-12-14T11:00:53.357 に答える
1

各ワーカー スレッドがファイルを処理した後、そのスレッドに関連付けられた条件変数を確認します。条件変数は、bool + クリティカル セクションとして単純に実装できます。または InterlockedExchange* 機能を使用します。正直なところ、私は通常、スレッド間で保護されていない bool を使用して、「終了する必要がある」ことを通知します。ワーカー スレッドがスリープしている可能性がある場合は、イベント ハンドルを使用することもあります。

各スレッドの条件変数を設定した後、メイン スレッドは WaitForSingleObject を介して各スレッドが終了するのを待ちます。

DWORD __stdcall WorkerThread(void* pThreadData)
{
    ThreadData* pData = (ThreadData*) pTheradData;

    while (pData->GetNeedToExit() == false)
    {
        ProcessNextFile();
    }
    return 0;
}

void StopWokerThread(HANDLE hThread, ThreadData* pData)
{
   pData->SetNeedToExit = true;
   WaitForSingleObject(hThread);
   CloseHandle(hThread);
}

struct ThreadData()
{
    CRITICAL_SECITON _cs;
    ThreadData()
    {
        InitializeCriticalSection(&_cs);
    }
    ~ThreadData()
    {
        DeleteCriticalSection(&_cs);
    }

    ThreadData::SetNeedToExit()
    {
        EnterCriticalSection(&_cs);
          _NeedToExit = true;
        LeaveCriticalSeciton(&_cs);
    }

    bool ThreadData::GetNeedToExit()
    {
        bool returnvalue;
        EnterCriticalSection(&_cs);
          returnvalue = _NeedToExit = true;
        LeaveCriticalSeciton(&_cs);
        return returnvalue;
    }

};
于 2012-12-14T11:09:19.477 に答える
1

I/O Completion ポートを使用して、スレッドのプールを使用し、それらの作業を調整することもできます。

通常、プールからのスレッドはスリープ状態になり、I/O 完了ポート イベント/アクティビティを待機します。リクエストがあると、I/O Completion ポートがスレッドを解放し、ジョブの実行を開始します。

于 2012-12-14T11:20:29.540 に答える
0

OK、これはどうですか:

ディスクごとに 2 つのスレッド。優先度の高い要求と低い要求に対応し、それぞれに独自の入力キューがあります。

優先度の高いディスク タスクは、最初に送信されると、実行中の優先度の低いタスクと並行してそのディスク要求を発行します。優先度の低いスレッドが可能なときに待機する ManualResetEvent をリセットすることができるため (WaitForSingleObject)、優先度の高いスレッドがディスク操作を実行している場合はブロックされます。優先度の高いスレッドは、タスクの終了後にイベントを設定する必要があります。

これにより、優先順位の高いタスクの送信と、優先順位の低いスレッドが MRE で待機できるときとの間の間隔 (存在する場合) に、ディスクのスラッシングが制限されます。優先順位の高いキューにサービスを提供するスレッドの CPU 優先順位を上げると、この間隔での優先順位の高い作業のパフォーマンスが向上する場合があります。

編集:「キュー」とは、スレッドセーフでブロッキングのプロデューサー/コンシューマーキューを意味します(明確にするために:)。

さらに編集 - 発行スレッドがジョブ完了の通知を必要とする場合、キューに発行されたタスクには、タスク オブジェクトをパラメーターとして呼び出す「OnCompletion」イベントを含めることができます。たとえば、イベント ハンドラーは、元のスレッドが待機している AutoResetEvent を通知して、同期通知を提供できます。

于 2012-12-14T11:56:23.843 に答える