2

我有一个有很多案例的应用程序。每个案例都有许多多页 tif 文件。我需要将 tf 文件转换为 pdf 文件。由于文件太多,我想我可以线程转换过程。我目前将该过程限制为一次十次转换(即十次踏板)。当一个转换完成时,另一个应该开始。

这是我正在使用的当前设置。

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 数组。List 的各个元素可以在单独的线程上访问,但是任何添加、删除或检查所有元素的操作(如 WaitAny 调用)都需要在锁内进行。

第二,不能保证您的代码一次只能处理 10 个文件。从检查 List 的大小到添加新项目的点之间的代码是开放的,供多个线程通过。

第三,在 QueueUserWorkItem 中启动的线程有可能转换同一个文件。如果不捕获循环内的文件名,转换文件的线程将在执行时使用文件名中的任何值,而不是调用 QueueUserWorkItem 时文件名中的任何值。

这篇 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 回答