【问题标题】:Threads that return data in .NET在 .NET 中返回数据的线程
【发布时间】:2009-08-06 16:21:48
【问题描述】:

我正在编写一个通常启动五个线程的程序。线程以不确定的顺序返回。每个线程都在调用一个返回 List 的方法。

我正在这样做:

var masterList = List<string>();    
foreach (var threadParam in threadParams)
{
    var expression = threadParam ;
    ThreadStart sub = () => MyMethod(expressions);
    var thread = new Thread(sub)
    {
        Name = expression
    };

    listThreads.Add(thread);
    thread.Start();
}

var abort = true;
while (abort) //Wait until all threads finish
{
    var count = 0;
    foreach (var list in listThreads)
    {
        if (!list.IsAlive)
        {
            count++;
        }
    }

    if (count == listThreads.Count)
    {
        abort = false;
    }
}

那么问题来了:

每个线程终止时都会返回一个列表,我想附加之前声明的 masterList。

如何处理这个问题?

我也知道必须有比下面更好的方法来等待所有线程完成

var abort = true;
while (abort) //Wait until all threads finish
{
    var count = 0;
    foreach (var list in listThreads)
    {
        if (!list.IsAlive)
        {
            count++;
        }
    }

    if (count == listThreads.Count)
    {
        abort = false;
    }
}

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

    使用WaitHandle


    这是一个例子:

    using System;
    using System.Threading;
    
    class ThreadSleeper
    {
        int seconds;
        AutoResetEvent napDone = new AutoResetEvent(false);
    
        private ThreadSleeper(int seconds)
        {
            this.seconds = seconds; 
        }
    
        public void Nap()
        {
            Console.WriteLine("Napping {0} seconds", seconds);
            Thread.Sleep(seconds * 1000);
            Console.WriteLine("{0} second nap finished", seconds);
            napDone.Set();
        }
    
        public static WaitHandle DoSleep(int seconds)
        {
            ThreadSleeper ts = new ThreadSleeper(seconds);
            Thread thread = new Thread(new ThreadStart(ts.Nap));
            thread.Start();
            return(ts.napDone);
        }
    }
    
    public class OperationsThreadsWaitingwithWaitHandle
    {
        public static void Main()
        {
            WaitHandle[] waits = new WaitHandle[2];
            waits[0] = ThreadSleeper.DoSleep(8);
            waits[1] = ThreadSleeper.DoSleep(4);
    
            Console.WriteLine("Waiting for threads to finish");
            WaitHandle.WaitAll(waits);
            Console.WriteLine("Threads finished");
        }
    }
    

    结帐链接

    【讨论】:

    • 和我的想法一样...但是更好的答案:(
    【解决方案2】:

    最好的方法是让每个线程都成为自己的对象。不要与其他对象混合,您只需构造它(传入变量),将自己添加为侦听器并启动它。

    完成后,它将值存储在成员变量中并通知您的侦听器。

    您的听众可以在闲暇时检索这些值。

    直接将值返回给侦听器的明显捷径可行,但您可能会发现此版本以后更灵活(实际上代码不多)

    【讨论】:

      【解决方案3】:

      您当然也可以使用普通代表和APM

      请注意,您所描述的模式通常被描述为 Future,这是一项承诺稍后会有所回报的后台作业(如果可能的话,是投资回报)​​。

      这是一个简短的例子,以控制台程序的形式。

      输出:

      sequential: 3224 ms
      parallel: 2074 ms
      

      当然,由于我没有构造显式线程,所以我将其留给线程池系统来计算并行运行的线程数。

      还有通过回调方法通知后台线程何时完成的规定,以及对显式检查和等待超时的 WaitHandle 支持等。

      来源:

      using System;
      using System.Collections.Generic;
      using System.Threading;
      using System.Diagnostics;
      
      namespace SO1215227
      {
          public class Program
          {
              public static void Main()
              {
                  Stopwatch sw = new Stopwatch();
                  sw.Start();
                  var list1 = Sequence(1, 100);
                  var list2 = Sequence(101, 200);
                  var list3 = Sequence(201, 300);
                  sw.Stop();
                  Console.Out.WriteLine("sequential: " + sw.ElapsedMilliseconds + " ms");
                  sw.Reset();
      
                  Func<Int32, Int32, List<Int32>> listProducer = Sequence;
                  sw.Start();
                  var list1Background = listProducer.BeginInvoke(1, 100, null, null);
                  var list2Background = listProducer.BeginInvoke(101, 200, null, null);
                  var list3Background = listProducer.BeginInvoke(201, 300, null, null);
      
                  list1 = listProducer.EndInvoke(list1Background);
                  list2 = listProducer.EndInvoke(list2Background);
                  list3 = listProducer.EndInvoke(list3Background);
                  sw.Stop();
                  Console.Out.WriteLine("parallel: " + sw.ElapsedMilliseconds + " ms");
      
                  Console.Out.Write("Press enter to exit...");
                  Console.In.ReadLine();
              }
      
              private static List<Int32> Sequence(Int32 from, Int32 to)
              {
                  List<Int32> result = new List<Int32>();
                  for (Int32 index = from; index <= to; index++)
                  {
                      result.Add(index);
                      Thread.Sleep(10); // simulate I/O wait
                  }
                  return result;
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        查看 WaitHandle 类和 WaitHandle.WaitAll(我在 WaitHandle 的一些代码中使用了 ManualResetEvent 类,但这只是一个示例。我不知道是否有更适合您的情况) .关闭所有五个线程,为它们提供对您的主列表的引用,添加时锁定此列表,然后发出完成信号。使用 WaitHandle.WaitAll 进行阻塞,直到所有五个都发出完成信号。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-02
          • 2013-11-16
          相关资源
          最近更新 更多