2

多くの場合があるアプリケーションがあります。それぞれのケースには、多くのマルチページtifファイルがあります。tfファイルをpdfファイルに変換する必要があります。ファイルがたくさんあるので、変換プロセスをスレッド化できると思いました。現在、プロセスを一度に10回の変換(つまり、10回のトレッド)に制限しています。1つの変換が完了すると、別の変換が開始されます。

これは私が使用している現在の設定です。

private void ConvertFiles()
{
  List<AutoResetEvent> semaphores = new List<AutoResetEvet>();
  foreach(String fileName in filesToConvert)
  {
    String file = fileName;

    if(semaphores.Count >= 10)
    {
      WaitHandle.WaitAny(semaphores.ToArray());
    }


    AutoResetEvent semaphore = new AutoResetEvent(false);
    semaphores.Add(semaphore);

    ThreadPool.QueueUserWorkItem(
      delegate
      { 
        Convert(file);
        semaphore.Set();
        semaphores.Remove(semaphore);
      }, null);
  }

  if(semaphores.Count > 0)
  {
    WaitHandle.WaitAll(semaphores.ToArray());
  }
}

これを使用すると、WaitHandle.WaitAll()またはWaitHandle.WaitAny()配列パラメーターが65の長さを超えてはならないという例外が発生することがあります。このアプローチで何が間違っているのでしょうか。

4

4 に答える 4

1

続行するには、WaitAny関数によってトリガーされたハンドルを削除する必要があるようです

if(semaphores.Count >= 10)
{
  int index = WaitHandle.WaitAny(semaphores.ToArray());
  semaphores.RemoveAt(index);
}

だから基本的に私は削除します:

semaphores.Remove(semaphore);

スレッドから呼び出し、上記を使用してシグナルイベントを削除し、それが機能するかどうかを確認します。

于 2010-02-04T02:21:25.387 に答える
1

実際のセマフォ(System.Threading)を使用していますか?セマフォを使用する場合、通常は最大リソースを割り当て、自動的にブロックします(追加および解放するときに)。WaitAnyアプローチを使用することもできますが、より難しいルートを選択したように感じます。

于 2010-02-04T02:22:58.707 に答える
1

あなたが書いたものにはいくつかの問題があります。

第一に、それはスレッドセーフではありません。AutoResetEventsの配列を追加、削除、待機する複数のスレッドがあります。リストの個々の要素には個別のスレッドでアクセスできますが、すべての要素を追加、削除、またはチェックするもの(WaitAny呼び出しなど)は、ロック内でアクセスする必要があります。

第2に、コードが一度に10個のファイルのみを処理するという保証はありません。リストのサイズがチェックされてから新しいアイテムが追加されるまでのコードは、複数のスレッドが通過できるように開かれています。

3番目に、QueueUserWorkItemで開始されたスレッドが同じファイルを変換する可能性があります。ループ内でfileNameをキャプチャしない場合、ファイルを変換するスレッドは、QueueUserWorkItemを呼び出したときにfileNameにあった値ではなく、実行時にfileNameにある値を使用します。

このcodeprojectの記事は、あなたがやろうとしていることの正しい方向を示しているはずです:http: //www.codeproject.com/KB/threads/SchedulingEngine.aspx

編集:

var semaphores = new List<AutoResetEvent>();
        foreach (String fileName in filesToConvert)
        {
            String file = fileName;
            AutoResetEvent[] array;
            lock (semaphores)
            {
                array = semaphores.ToArray();
            }
            if (array.Count() >= 10)
            {
                WaitHandle.WaitAny(array);
            }

            var semaphore = new AutoResetEvent(false);
            lock (semaphores)
            {
                semaphores.Add(semaphore);
            }
            ThreadPool.QueueUserWorkItem(
              delegate
              {
                  Convert(file);
                  lock (semaphores)
                  {
                      semaphores.Remove(semaphore);
                  }
                  semaphore.Set();
              }, null);
        }

個人的には、このようにすることはないと思います...しかし、あなたが持っているコードで作業すれば、これはうまくいくはずです。

于 2010-02-04T04:57:20.720 に答える
0

そんなに多くのイベントを作成するべきではないでしょうか?

// input
var filesToConvert = new List<string>();
Action<string> Convert = Console.WriteLine;

// limit
const int MaxThreadsCount = 10;

var fileConverted = new AutoResetEvent(false);
long threadsCount = 0;

// start
foreach (var file in filesToConvert) {
    if (threadsCount++ > MaxThreadsCount) // reached max threads count 
        fileConverted.WaitOne();          // wait for one of started threads

    Interlocked.Increment(ref threadsCount);

    ThreadPool.QueueUserWorkItem(
        delegate {
            Convert(file);

            Interlocked.Decrement(ref threadsCount);
            fileConverted.Set();
        });
}

// wait
while (Interlocked.Read(ref threadsCount) > 0) // paranoia?
    fileConverted.WaitOne();
于 2010-02-04T02:52:39.043 に答える