【问题标题】:What's the difference between Thread Delegate and Define a action by lambda expression in Thread [duplicate]Thread Delegate和Define a action by lambda expression in Thread有什么区别[重复]
【发布时间】:2018-01-09 02:22:48
【问题描述】:

我知道在 C# .Net 中创建线程的两种方法,您可以使用ThreadStart 委托,同时定义 lambda 表达式也可以完成这项工作。

假设我有一个类定义为:

class myWorkerClass{

    public myWorkerClass(string configInfo) {

    }

    public void DoWork(ConcurrentQueue<string> executionBuffer) {

    }

    public void DoWork2() {

    }

}

我将在main函数中构造myWorkerClass,并为每个myWorkerClass对象构造线程来运行DoWork方法:

class Program {

     static void Main(string args[]) {
         int nodeCount = 0;
         using (var reader = XmlReader.Create("config.xml"))
         {
             while (reader.Read())
             {
                 if (reader.NodeType == XmlNodeType.Element &&
                     reader.Name == "worker")
                 {
                     nodeCount++;
                 }
             }
         }

         myWorkerClass[] obj = new myWorkerClass[nodeCount];
         XmlDocument doc = new XmlDocument();
         doc.Load("config.xml");

         int call = 0;

         foreach (XmlNode node in doc.DocumentElement.ChildNodes)
         {
             // Code to parse xml and get configInfo

             obj[call] = new myWorkerClass(configInfo);

             call++;
         }

         ConcurrentQueue<string> bufferExecutions = new ConcurrentQueue<string>();

         for (int i = 0; i < call; ++i)
         {
             //Thread workThread = new Thread(new ThreadStart(obj[i].DoWork2));
             Thread workThread = new Thread(() => obj[i].DoWork(bufferExecutions));
             workThread.IsBackground = true;
             workThread.Start();
         }

    }

}

我的问题是创建线程的 lambda 表达式方式给了我Index was outside the bounds of the array. 的错误,但是当我使用ThreadStart 委托创建新线程时,我的代码工作正常。我想知道导致我的问题的这两种机制有什么不同?

【问题讨论】:

  • Lambda 捕获变量而不是值。
  • @PetSerAI,非常感谢!很棒的评论。解释了一切

标签: c# .net multithreading lambda


【解决方案1】:

一般来说,使用 ThreadStart 委托或使用 lambda 表达式开始新的 Thread 没有区别。编译器将 lambda 表达式隐式转换为委托,并以与 ThreadStart 委托相同的方式将其传递给 Thread 构造函数。

不同之处在于您的 lambda 表达式捕获变量i。而且(正如@PetSerAl 所指出的)它不会捕获i 的当前值,而是捕获变量i 本身。有关捕获变量的说明,请参见例如 Closing over the loop variable considered harmful

ThreadStart 代码的变体将评估 i 的值 new Thread() 构造函数被调用(在主线程上)。但是对于 lambda 表达式变体,i 将在执行 lambda 表达式时在新创建的线程上进行计算(因此 after new Thread() 构造函数被调用)。因此,您的某些线程可能会使用错误的 i 值,具体取决于在线程实际启动之前执行了多少个循环周期。最后,如果其中一个线程在循环的最后一个循环之后执行(在++i 的最终执行之后),i 将指向obj 数组的最后一个元素,抛出异常。

这可以通过引入新变量(例如j在循环中轻松解决,并将i 的值复制到它。因为j是在循环内部声明的,所以循环的每个循环都会有自己的j变量,所以它们的值不会在每个循环中改变,使得lambda表达式捕获按预期工作。

 for (int i = 0; i < call; ++i)
 {
     // Each cycle of the for loop creates new 'j' variable,
     // thus we can safely capture it in lambda expression,
     // because it's value will not be overwritten in the next cycle
     var j = i;

     // use j instead of i
     Thread workThread = new Thread(() => obj[j].DoWork(bufferExecutions));
     workThread.IsBackground = true;
     workThread.Start();
 }

从 C# 5.0 开始,您还可以将 for 循环转换为 foreach。有一个突破性的变化,“修复”了这个问题:

 // This will work correctly only with C# 5.0 or newer
 foreach(var current_obj in obj)
 {
     Thread workThread = new Thread(() => current_obj.DoWork(bufferExecutions));
     workThread.IsBackground = true;
     workThread.Start();
 }

【讨论】:

  • 有趣的是,foreach 循环的性能比添加局部变量来获取索引的副本要慢。
猜你喜欢
  • 2013-05-22
  • 1970-01-01
  • 2012-01-22
  • 2012-01-24
  • 1970-01-01
  • 2014-09-02
  • 1970-01-01
相关资源
最近更新 更多