在本节中,使用任务执行基本的操作,并且获取任务执行完成后的结果值。本节内容比较简单,在此不做过多介绍。
演示代码如下,在主线程中要获取结果值,常用的方式就是访问task.Result属性,如果任务线程还没执行完毕,那么会阻塞主线程,直到线程执行完。如果任务线程执行完毕,那么将直接拿到运算的结果值。
在Task 3中,使用了task.Status来打印线程的状态,线程每个状态的具体含义,将在下一节中介绍。
static void Main(string[] args) { // 直接执行方法 作为参照 TaskMethod("主线程任务"); // 访问 Result属性 达到运行结果 Task<int> task = CreateTask("Task 1"); task.Start(); int result = task.Result; WriteLine($"运算结果: {result}"); // 使用当前线程,同步执行任务 task = CreateTask("Task 2"); task.RunSynchronously(); result = task.Result; WriteLine($"运算结果:{result}"); // 通过循环等待 获取运行结果 task = CreateTask("Task 3"); WriteLine(task.Status); task.Start(); while (!task.IsCompleted) { WriteLine(task.Status); Sleep(TimeSpan.FromSeconds(0.5)); } WriteLine(task.Status); result = task.Result; WriteLine($"运算结果:{result}"); Console.ReadLine(); } static Task<int> CreateTask(string name) { return new Task<int>(() => TaskMethod(name)); } static int TaskMethod(string name) { WriteLine($"{name} 运行在线程 {CurrentThread.ManagedThreadId}上. 是否为线程池线程 {CurrentThread.IsThreadPoolThread}"); Sleep(TimeSpan.FromSeconds(2)); return 42; }运行结果如下,可见Task 1 和Task 2均是运行在主线程上,并非线程池线程。
1.4 组合任务在本节中,体现了任务其中一个强大的功能,那就是组合任务。通过组合任务可很好的描述任务与任务之间的异步、同步关系,大大降低了编程的难度。
组合任务主要是通过task.ContinueWith()、task.WhenAny()、task.WhenAll()等和task.GetAwaiter().OnCompleted()方法来实现。
在使用task.ContinueWith()方法时,需要注意它也可传递一系列的枚举选项TaskContinuationOptions,该枚举选项和TaskCreationOptions类似,其具体定义如下表所示。
成员名称 说明AttachedToParent 如果延续为子任务,则指定将延续附加到任务层次结构中的父级。 只有当延续前面的任务也是子任务时,延续才可以是子任务。 默认情况下,子任务(即由外部任务创建的内部任务)将独立于其父任务执行。 可以使用 TaskContinuationOptions.AttachedToParent 选项以便将父任务和子任务同步。请注意,如果使用 DenyChildAttach 选项配置父任务,则子任务中的 AttachedToParent 选项不起作用,并且子任务将作为分离的子任务执行。有关更多信息,请参见Attached and Detached Child Tasks。
DenyChildAttach 指定任何使用 TaskCreationOptions.AttachedToParent 选项创建,并尝试作为附加的子任务执行的子任务(即,由此延续创建的任何嵌套内部任务)都无法附加到父任务,会改成作为分离的子任务执行。 有关详细信息,请参阅附加和分离的子任务。
ExecuteSynchronously 指定应同步执行延续任务。 指定此选项后,延续任务在导致前面的任务转换为其最终状态的相同线程上运行。如果在创建延续任务时已经完成前面的任务,则延续任务将在创建此延续任务的线程上运行。 如果前面任务的 CancellationTokenSource 已在一个 finally(在 Visual Basic 中为 Finally)块中释放,则使用此选项的延续任务将在该 finally 块中运行。 只应同步执行运行时间非常短的延续任务。由于任务以同步方式执行,因此无需调用诸如 Task.Wait 的方法来确保调用线程等待任务完成。
HideScheduler 指定由延续通过调用方法(如 Task.Run 或 Task.ContinueWith)创建的任务将默认计划程序 (TaskScheduler.Default) 视为当前的计划程序,而不是正在运行该延续的计划程序。
LazyCancellation 在延续取消的情况下,防止延续的完成直到完成先前的任务。
LongRunning 指定延续将是长期运行的、粗粒度的操作。 它会向 TaskScheduler 提示,过度订阅可能是合理的。
None 如果未指定延续选项,应在执行延续任务时使用指定的默认行为。 延续任务在前面的任务完成后以异步方式运行,与前面任务最终的 Task.Status 属性值无关。 如果延续为子任务,则会将其创建为分离的嵌套任务。
NotOnCanceled 指定不应在延续任务前面的任务已取消的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Canceled,则前面的任务会取消。 此选项对多任务延续无效。
NotOnFaulted 指定不应在延续任务前面的任务引发了未处理异常的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Faulted,则前面的任务会引发未处理的异常。 此选项对多任务延续无效。
NotOnRanToCompletion 指定不应在延续任务前面的任务已完成运行的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.RanToCompletion,则前面的任务会运行直至完成。 此选项对多任务延续无效。
OnlyOnCanceled 指定只应在延续前面的任务已取消的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Canceled,则前面的任务会取消。 此选项对多任务延续无效。
OnlyOnFaulted 指定只有在延续任务前面的任务引发了未处理异常的情况下才应安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Faulted,则前面的任务会引发未处理的异常。OnlyOnFaulted 选项可保证前面任务中的 Task.Exception 属性不是 null。 你可以使用该属性来捕获异常,并确定导致任务出错的异常。 如果你不访问 Exception 属性,则不会处理异常。 此外,如果尝试访问已取消或出错的任务的 Result 属性,则会引发一个新异常。此选项对多任务延续无效。
OnlyOnRanToCompletion 指定只应在延续任务前面的任务已完成运行的情况下才安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.RanToCompletion,则前面的任务会运行直至完成。 此选项对多任务延续无效。
PreferFairness 提示 TaskScheduler 按任务计划的顺序安排任务,因此较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。
RunContinuationsAsynchronously 指定应异步运行延续任务。 此选项优先于 TaskContinuationOptions.ExecuteSynchronously。