更新:2011 年 3 月
任务提供两个主要好处:
-
系统资源的使用效率更高,可伸缩性更好。
为了补偿这一点,可使用众所周知的工作窃取算法提供负载平衡。
-
对于线程或工作项,可以使用更多的编程控件。
任务和围绕它们生成的框架提供了一组丰富的 API,这些 API 支持等待、取消、继续、可靠的异常处理、详细状态、自定义计划等功能。
出于这两个原因,在 .NET Framework 4 中,任务是用于编写多线程、异步和并行代码的首选 API。
TaskStatus 枚举表示。
lambda 表达式可以包含对命名方法的调用,如下面的示例所示。
// Create a task and supply a user delegate by using a lambda expression. var taskA = new Task(() => Console.WriteLine("Hello from taskA.")); // Start the task. taskA.Start(); // Output a message from the joining thread. Console.WriteLine("Hello from the calling thread."); /* Output: * Hello from the joining thread. * Hello from taskA. */
如果不必将创建和计划分开,则这是创建和启动任务的首选方法,如下面的示例所示
Result,则该属性将阻止线程,直到值可用为止。
Task<double>[] taskArray = new Task<double>[] { Task<double>.Factory.StartNew(() => DoComputation1()), // May be written more conveniently like this: Task.Factory.StartNew(() => DoComputation2()), Task.Factory.StartNew(() => DoComputation3()) }; double[] results = new double[taskArray.Length]; for (int i = 0; i < taskArray.Length; i++) results[i] = taskArray[i].Result;
如何:从任务中返回值。
通过使用构造函数向任务提供状态对象,可以访问每次迭代的值,如下面的示例所示:
class MyCustomData { public long CreationTime; public int Name; public int ThreadNum; } void TaskDemo2() { // Create the task object by using an Action(Of Object) to pass in custom data // in the Task constructor. This is useful when you need to capture outer variables // from within a loop. As an experiement, try modifying this code to // capture i directly in the lambda, and compare results. Task[] taskArray = new Task[10]; for(int i = 0; i < taskArray.Length; i++) { taskArray[i] = new Task((obj) => { MyCustomData mydata = (MyCustomData) obj; mydata.ThreadNum = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Hello from Task #{0} created at {1} running on thread #{2}.", mydata.Name, mydata.CreationTime, mydata.ThreadNum) }, new MyCustomData () {Name = i, CreationTime = DateTime.Now.Ticks} ); taskArray[i].Start(); } }
另外,在某些情况下,通过构造函数传递数据可以获得少量性能改进。
Result 属性中的程序。
Task<byte[]> getData = new Task<byte[]>(() => GetFileData());
Task<double[]> analyzeData = getData.ContinueWith(x => Analyze(x.Result));
Task<string> reportData = analyzeData.ContinueWith(y => Summarize(y.Result));
getData.Start();
//or...
Task<string> reportData2 = Task.Factory.StartNew(() => GetFileData())
.ContinueWith((x) => Analyze(x.Result))
.ContinueWith((y) => Summarize(y.Result));
System.IO.File.WriteAllText(@"C:\reportFolder\report.txt", reportData.Result);
如何:用延续将多个任务链接在一起。
下面的示例演示一个任务,该任务创建一个分离的嵌套任务。
var outer = Task.Factory.StartNew(() => { Console.WriteLine("Outer task beginning."); var child = Task.Factory.StartNew(() => { Thread.SpinWait(5000000); Console.WriteLine("Detached task completed."); }); }); outer.Wait(); Console.WriteLine("Outer task completed."); /* Output: Outer task beginning. Outer task completed. Detached task completed. */
请注意,外部任务不会等待嵌套任务完成。
下面的示例演示一个任务,该任务创建一个子任务:
var parent = Task.Factory.StartNew(() => { Console.WriteLine("Parent task beginning."); var child = Task.Factory.StartNew(() => { Thread.SpinWait(5000000); Console.WriteLine("Attached child completed."); }, TaskCreationOptions.AttachedToParent); }); parent.Wait(); Console.WriteLine("Parent task completed."); /* Output: Parent task beginning. Attached task completed. Parent task completed. */
嵌套任务和子任务。