1

で単純なデリゲート アクションを数回開始したいと考えていThreadPoolます。問題は、すべてのアクションが完了するまで待つ必要があることです。それをどのように処理しますか?

Action<int> someAction = i => { /* do something */ }
foreach (var yIndex in yRange)
{
    foreach (var xIndex in xRange)
    {
        // ThreadPool.QueueUserWorkItem(??? someAction(yIndex)) - how to start someAction(int) in a thread pool
    }

    WaitHandle.WaitAll(doneEvents); // how to wait for to finish?
}

ManualResetEvent新しいクラスを使用してコールバックを作成できることはわかっています。しかし問題はsomeAction(int)、メインクラスに実装されている多くのメソッドを使用していることです...

4

3 に答える 3

2

タスク並列ライブラリを使用できます。

List<Action> actions = new List<Action>();

foreach(var yIndex in yRange)
  foreach(var xIndex in xRange)
    actions.Add(() => someAction(yIndex));

Parallel.Invoke(actions.ToArray());

Parallel.Invokeは、すべてのタスクが完了すると戻り、可能な場合は並列で実行します (スレッド プールのように)。

于 2013-08-20T10:59:56.857 に答える
1

Matten のアドバイスに従い、TPL メカニズムを使用してください。TPL でこれを行うには、さまざまな有効な方法があります。

古い学校の方法を使用してこれを行う方法に興味がある場合は、読み続けてください。一般的なパターンは、単一のシグナリング メカニズムを使用することです。for each 操作の作成WaitHandleはまったくスケーラブルではありません。実際、このWaitHandle.WaitAllメソッドはとにかく 64 個のハンドルしか受け入れません。

foreach (var yIndex in yRange)
{
    var yclosure = yIndex;
    var finished = new CountdownEvent(1);
    foreach (var xIndex in xRange)
    {
        var xclosure = xIndex;
        finished.AddCount();
        ThreadPool.QueueUserWorkItem(
          (state) =>
          {
            try
            {
              DoSomething(yclosure, xclosure);
            }
            finally
            {
              finished.Signal();
            }
          }, null);
    }
    finished.Signal();
    finished.Wait();
}

上記のパターンはCountdownEvent、シグナリング メカニズムとして使用します。intカウンターとInterlockedメソッドも使用できます。このパターンを使用するときによくある間違いが 2 つあります。

  1. メイン スレッドは、他のスレッドと同様に並列タスクであるかのように扱う必要があります。つまり、シグナルを 1 カウントで初期化する必要があります (メイン スレッドを表すため)。そしてSignal、ループの最後で、メイン スレッドがタスクのキューイングを完了したことを通知します。このアドバイスに従わないと、次のタスクがキューに入れられる前に 1 つのタスクが終了すると、非常に微妙な競合状態が発生する可能性が生じます。1

  2. ラムダ式または匿名デリゲートで使用するループ変数を閉じるには、別の変数を作成する必要があります。クロージャは値ではなく変数をキャプチャするため、特別なキャプチャ変数を使用しないと、タスクが想定どおりので動作しない可能性があることに注意してください


1この特定のケースAddCountでは、イベントがすでに通知されている場合、実際には例外がスローされます。

于 2013-08-21T15:02:52.857 に答える
0

Semaphore オブジェクトを使用して目標を達成できると思います。これを試して:

int numActions = xRange.Count * yRange.Count; // Set the total number of actions
Semaphore resCount = new Semaphore(0, numActions);
Action<int> someAction = i => { try {/* do something */} finally {resCount.Release(1);} }
// ... the big loop adding actions
resCount.WaitOne();

コードは正確ではありませんが、アイデアは機能するはずです。

于 2013-08-20T11:07:43.127 に答える