Thread代表和Thread中的lambda表达式定义动作之间的区别是什么

时间:2018-01-09 02:22:48

标签: c# .net multithreading lambda

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

假设我有一个定义为:

的类
class myWorkerClass{

    public myWorkerClass(string configInfo) {

    }

    public void DoWork(ConcurrentQueue<string> executionBuffer) {

    }

    public void DoWork2() {

    }

}

我将在myWorkerClass函数中构造main并为每个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委托创建新线程时,我的代码工作正常。我想知道造成我问题的这两种机制之间有什么不同?

1 个答案:

答案 0 :(得分:0)

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

区别在于您的lambda表达式捕获变量i。并且(正如@PetSerAl指出的那样)并没有捕获i的当前值,而是捕获变量i本身。有关捕获变量的说明,请参阅示例Closing over the loop variable considered harmful

代码的

ThreadStart变体将在调用 i构造函数之前(在主线程上)评估new Thread() 的值。但是对于lambda表达式变体,当执行lambda表达式时,将在新创建的线程上计算i(因此在调用 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。有一个突破性的变化,&#34;修复&#34;这个问题:

 // 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();
 }