【问题标题】:About the Task.StartNew(Action<Object>, Object) method关于 Task.StartNew(Action<Object>, Object) 方法
【发布时间】:2014-03-21 05:18:28
【问题描述】:

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

我正在阅读此页面:Task Parallelism (Task Parallel Library)

在一个部分中,它说以下代码是正确的解决方案,因为循环中的 lambda 在每次迭代后都会发生变异时无法获得值,而是最终值。因此,您应该将“i”包装在 CustomData 对象中。代码如下:

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 < taskArray.Length; i++)
      {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) =>
            {
               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()方法中,作者使用了它的一种重载形式:

Task.StartNew(Action<Object>, Object)

我很想知道“obj”从何而来?它如何具有 3 个属性:Name、CreationTime 和 ThreadNum。

我在 MSDN 上进行了搜索,没有发现任何有用的详细信息,但这是:“一个包含要由操作委托使用的数据的对象。” MSDN 真的什么都解释不了。

谁能解释一下?

【问题讨论】:

    标签: c# .net multithreading task-parallel-library task


    【解决方案1】:

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

    void StartNew(Action<object> 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

    【讨论】:

    • 一个例子来统治他们!它解释了一切。非常感谢!
    【解决方案2】:

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

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

    SendOrPostCallback 类型的回调 lambda 将在将来调用,“Hello”为 state

    state 参数可以用作优化,但它并不是真正需要的,这里和Task.Factory.StartNew 或在大多数其他情况下都提供了state 参数。

    您的 lambda 是一个 闭包,它可以访问外部作用域的局部变量,因此下面会产生相同的结果,而无需显式传递 state

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

    这同样适用于Task.Factory.StartNew。为此,Task.Factory.StartNew 提供了一组覆盖 accept non-generic action Action,而不是 Action&lt;object&gt;

    因此,您的代码可能看起来像这样,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);
         });
      }
    

    【讨论】:

    • 感谢您的解释 :) 这真的很有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-03
    • 2015-06-09
    • 1970-01-01
    • 2013-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多