c# - 运行多个线程,在另一个线程完成时启动新线程

标签 c# multithreading waithandle

我有一个包含很多案例的应用程序。每个案例都有许多多页 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。我在这种方法中做错了什么以及如何纠正它?

最佳答案

你写的内容有一些问题。

第一,它不是线程安全的。您有多个线程添加、删除和等待 AutoResetEvents 数组。 List 的各个元素可以在单独的线程上访问,但是添加、删除或检查所有元素的任何操作(如 WaitAny 调用)都需要在锁内执行。

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

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

这篇代码项目文章应该为您指明您想要做的事情的正确方向: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);
        }

就我个人而言,我不认为我会这样做......但是,使用您拥有的代码,这应该可行。

关于c# - 运行多个线程,在另一个线程完成时启动新线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2196945/

相关文章:

java - 正确同步的程序是否仍然允许数据竞争?(第一部分)

.net - 退出上下文对于 WaitHandle.WaitOne 意味着什么?

.net - 互斥体是否正确实现以及如何处理它?

ruby - 通过推送通知唤醒

java - Java并发同步问题

c# - 为什么我的 NamedPipeServerStream 不等待?

c# - PooledRedisClientManager 不释放连接

c# - AppDomains 是否在自己的线程中执行?

c# - 使用 GridView1_SelectedIndexChanged 时出错

c# - ASP Core HttpClientFactory 模式使用客户端证书