【问题标题】:C# multi-threaded console application - Console quits before threads completeC# 多线程控制台应用程序 - 控制台在线程完成之前退出
【发布时间】:2011-10-13 18:52:41
【问题描述】:

我有一个最多可创建 5 个线程的 c# 控制台应用程序。

线程执行良好,但 UI 线程在完成工作时关闭。

有没有办法让主 UI 线程在运行时保持主 UI 线程运行?

foreach (var url in urls)
{
    Console.WriteLine("starting thread: " + url); 
    ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(myMethod), url);
}

我正在按照上面的代码开始我的线程。

【问题讨论】:

  • 如果你创建普通线程,而不是使用线程池呢?
  • 什么版本的 .NET?如果 .NET 4.0 我推荐 TPL 任务,将所有任务存储在一个数组中,并在退出前使用 Tasks.WaitAll(myTaskArray)。
  • 我认为线程需要是BackgroundWorker=false
  • @James 应该将其发布为答案,顺便说一句,在博客上做得很好 :)

标签: c# multithreading


【解决方案1】:

ThreadPool 中的线程是后台线程,这意味着退出的应用程序不会等待它们完成。

你有几个选择:

  • 等待例如信号量
  • 使用 Sleep() 在计数器上等待,非常粗糙,但对于简单的控制台应用程序来说还可以。
  • 使用 TPL,Parallel.ForEach(urls, url => MyMethod(url));

【讨论】:

  • +1,但我根本不推荐 Counter + Sleep .. 还值得注意的是,可以使用 CountDownEvent
  • @Lirik CountDownEv 是 Fx4+,那么你不妨使用 TPL。
  • @Henk,TPL 有一定的开销,如果任务很短,那么 TPL 效率不高。在这些情况下,最好使用ThreadPool 或者只使用一些专用的消费者线程来处理任务。您可以在这两种情况下使用CountDownEvent,或者只在线程上调用Join...
  • @lirik :OP 正在处理 URL,我很确定 Task 是负担得起的。
【解决方案2】:

如果您使用的是 .NET 4.0:

var tasks = new List<Task>();

foreach(var url in urls)
{
    tasks.Add(Task.Factory.StartNew(myMethod, url));
}

// do other stuff...

// On shutdown, give yourself X number of seconds to wait for them to complete...
Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(30));

【讨论】:

    【解决方案3】:

    啊 - 线程池是后台。它已排队,但随后您的程序结束。完成的。程序终止。

    阅读信号量 (WaitSignal) 并等待 - 回调中的线程在结束时发出信号,当所有线程都发出信号表明主线程可以继续时。

    【讨论】:

      【解决方案4】:

      如果您使用的是 .net 4,那么:

      urls.AsParallel().ForAll(MyMethod);
      

      在 .net 4 之前启动单独的线程,将它们保存在一个列表中并调用 Join()。工作人员不是后台的事实会在主线程退出后让他们保持活力,但 Join() 更明确。

              List<Thread> workers = new List<Thread>();
              foreach(var url in urls)
              {
                  Thread t = new Thread(MyMethod) {IsBackground = false};
                  workers.Add(t);
                  t.Start(url);
              }
      
              foreach (var worker in workers)
              {
                  worker.Join();
              }
      

      【讨论】:

        【解决方案5】:

        解决问题的最简单方法。

        在你的程序类中:

        static volatile int ThreadsComplete = 0;
        

        在你的“myMethod”最后返回之前:

        //ThreadsComplete++; //*edit* for safety's sake
        Interlocked.Increment(ref ThreadsComplete);
        

        在你的 main 方法返回/结束之前:

        while(ThreadsComplete < urls.Count) { Thread.Sleep(10); }
        

        以上内容本质上是一个 WaitForAll 同步方法。

        【讨论】:

        • 这就是为什么你有一个CountdownEvent... 不需要循环或休眠,无论如何等待都是不好的做法。
        • @Henk Holterman & Lirik,你不同意“不良做法”比“错误答案”更主观一点。这个答案被称为 hack,OP 批准了它。所以,我不明白你试图说明什么。此外,CountDownEvent 是 .Net 的一个相当新的补充,在这种特定情况下,会比仅使用 Parallel ForEach 产生更多的编程开销。那么,在这种情况下,CountdownEvent 也不是“最佳实践”吗?
        【解决方案6】:

        在主要:

        var m = new ManualResetEvent(false);
        // do something
        foreach (var url in urls)
        {
          Console.WriteLine("starting thread: " + url); 
          ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(myMethod), url);
        }
        m.WaitOne();
        
        
        private static void myMethod(object obj)
        {
          try{
           // do smt
          }
          finally {
            m.Set();
          }
        }
        

        【讨论】:

        • 如果他将ManualResetEvent 更改为CountDownEvent,那么这将是一个很好的解决方案。
        • @Lirik 有了足够的变化,它也可以写一首诗了
        • 是的,当然 CountDownEvent 是真实的。永远不要低估它!
        猜你喜欢
        • 2012-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-07
        • 1970-01-01
        • 2011-07-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多