关于Task.StartNew(Action ,Object)方法

我在这个页面上学习TPL,一个代码块让我很困惑。

我正在阅读此页面: 任务并行(任务并行库)

在一节中,它说下面的代码是正确的解决方案,因为循环中的lambda不能获得每次迭代后变异的值,而是最终值。 所以你应该在CustomData对象中包装“i”。 代码如下:

class CustomData { public long CreationTime; public int Name; public int ThreadNum; } public class Example { public static void Main() { // Create the task object by using an Action(Of Object) to pass in custom data // to the Task constructor. This is useful when you need to capture outer variables // from within a loop. Task[] taskArray = new Task[10]; for (int i = 0; i  { CustomData data = obj as CustomData; if (data == null) return; data.ThreadNum = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Task #{0} created at {1} on thread #{2}.", data.Name, data.CreationTime, data.ThreadNum); }, new CustomData() { Name = i, CreationTime = DateTime.Now.Ticks }); } Task.WaitAll(taskArray); } } 

代码相当简单易懂,但问题出现了:

在Task.Factory.StartNew()方法中,作者使用其重载forms之一:

 Task.StartNew(Action, Object) 

我很好奇,知道“obj”来自哪里? 它是如何具有3个属性的 :Name,CreationTime和ThreadNum。

我搜索了MSDN,发现没有有用的细节,但是:“一个包含动作委托使用的数据的对象。” MSDN它真的没有解释任何事情。

谁能解释一下呢?

这是一个更简洁的例子,可能有助于解释它。

 void StartNew(Action action, object o) { action(o); } 

StartNew方法只接受action委托并通过传递o作为参数来调用它。 传递给lambda的值只是在lambda之后传递给StartNew的值

 // Prints "hello world" StartNew(o => Console.WriteLine(o), "hello world"); 

在这种情况下,您概述了作为第二个参数传递的值

 new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} 

这只是创建一个CustomData的新对象,为它赋予一些属性,并使其成为紧接在它之前定义的lambda的参数。 它最终将成为lambda中的价值obj

这是将不透明状态对象传递给回调的标准模式,它在.NET框架中的许多其他位置使用。 一个更简单的例子, SendOrPostCallback

 SynchronizationContext.Current.Post(state => MessageBox.Show(state.ToString()), state: "Hello"); 

将来以“Hello”作为state调用SendOrPostCallback类型的回调lambda。

state参数可以用作优化,但它既不是必需的,也不是Task.Factory.StartNew或者在大多数其他情况下都提供了state参数。

你的lambda是一个闭包 ,它可以访问外部作用域局部变量,所以下面产生相同的结果,而不显式传递state

 var message = "Hello"; SynchronizationContext.Current.Post(_ => MessageBox.Show(message), state: null); 

这同样适用于Task.Factory.StartNew 。 为此, Task.Factory.StartNew提供了一组覆盖,它们接受非generics操作Action ,而不是Action

所以,你的代码可能看起来像这样,这是IMO更具可读性:

  for (int i = 0; i < taskArray.Length; i++) { var data = new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks}; taskArray[i] = Task.Factory.StartNew(() => { if (data == null) return; data.ThreadNum = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Task #{0} created at {1} on thread #{2}.", data.Name, data.CreationTime, data.ThreadNum); }); }