上海工程建设造价信息网站知识营销

news/2025/9/30 14:37:54/文章来源:
上海工程建设造价信息网站,知识营销,微信群 网站建设,用什么网站做海报目录 1.1 简介1.2 创建任务1.3 使用任务执行基本的操作1.4 组合任务1.5 将APM模式转换为任务1.6 将EAP模式转换为任务1.7 实现取消选项1.8 处理任务中的异常1.9 并行运行任务1.10 使用TaskScheduler配置任务执行参考书籍笔者水平有限#xff0c;如果错误欢迎各位批评指正如果错误欢迎各位批评指正本系列首页链接[C#多线程编程系列一- 简介 ] 1.1 简介# 在之前的几个章节中就线程的使用和多线程相关的内容进行了介绍。因为线程涉及到异步、同步、异常传递等问题所以在项目中使用多线程的代价是比较高昂的需要编写大量的代码来达到正确性和健壮性。 为了解决这样一些的问题在.Net Framework 4.0中引入了一个关于一步操作的API。它叫做任务并行库(Task Parallel Library)。然后在.Net Framwork 4.5中对它进行了轻微的改进本文的案例都是用最新版本的TPL库而且我们还可以使用C# 5.0的新特性await/async来简化TAP编程当然这是之后才介绍的。 TPL内部使用了线程池但是效率更高。在把线程归还回线程池之前它会在同一线程中顺序执行多少Task这样避免了一些小任务上下文切换浪费时间片的问题。 任务是对象其中封装了以异步方式执行的工作但是委托也是封装了代码的对象。任务和委托的区别在于委托是同步的而任务是异步的。 在本章中我们将会讨论如何使用TPL库来进行任务之间的组合同步如何将遗留的APM和EAP模式转换为TPL模式等等。 1.2 创建任务# 在本节中主要是演示了如何创建一个任务。其主要用到了System.Threading.Tasks命名空间下的Task类。该类可以被实例化并且提供了一组静态方法可以方便快捷的创建任务。 在下面实例代码中分别延时了三种常见的任务创建方式并且创建任务是可以指定任务创建的选项从而达到最优的创建方式。 在TaskCreationOptions中一共有7个枚举枚举是可以使用|运算符组合定义的。其枚举如下表所示。 成员名称说明AttachedToParent指定将任务附加到任务层次结构中的某个父级。 默认情况下子任务即由外部任务创建的内部任务将独立于其父任务执行。 可以使用 TaskContinuationOptions.AttachedToParent 选项以便将父任务和子任务同步。请注意如果使用 DenyChildAttach 选项配置父任务则子任务中的 AttachedToParent 选项不起作用并且子任务将作为分离的子任务执行。有关详细信息请参阅附加和分离的子任务。DenyChildAttach指定任何尝试作为附加的子任务执行即使用 AttachedToParent 选项创建的子任务都无法附加到父任务会改成作为分离的子任务执行。 有关详细信息请参阅附加和分离的子任务。HideScheduler防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 Default 当前计划程序。LongRunning指定任务将是长时间运行的、粗粒度的操作涉及比细化的系统更少、更大的组件。 它会向 TaskScheduler 提示过度订阅可能是合理的。 可以通过过度订阅创建比可用硬件线程数更多的线程。 它还将提示任务计划程序该任务需要附加线程以使任务不阻塞本地线程池队列中其他线程或工作项的向前推动。None指定应使用默认行为。PreferFairness提示 TaskScheduler 以一种尽可能公平的方式安排任务这意味着较早安排的任务将更可能较早运行而较晚安排运行的任务将更可能较晚运行。RunContinuationsAsynchronously强制异步执行添加到当前任务的延续任务。请注意RunContinuationsAsynchronously 成员在以 .NET Framework 4.6 开头的 TaskCreationOptions 枚举中可用。Copy static void Main(string[] args) { // 使用构造方法创建任务 var t1 new Task(() TaskMethod(Task 1)); var t2 new Task(() TaskMethod(Task 2)); // 需要手动启动 t2.Start(); t1.Start(); // 使用Task.Run 方法启动任务 不需要手动启动 Task.Run(() TaskMethod(Task 3)); // 使用 Task.Factory.StartNew方法 启动任务 实际上就是Task.Run Task.Factory.StartNew(() TaskMethod(Task 4)); // 在StartNew的基础上 添加 TaskCreationOptions.LongRunning 告诉 Factory该任务需要长时间运行 // 那么它就会可能会创建一个 非线程池线程来执行任务 Task.Factory.StartNew(() TaskMethod(Task 5), TaskCreationOptions.LongRunning); ReadLine(); } static void TaskMethod(string name) { WriteLine($任务 {name} 运行线程 id {CurrentThread.ManagedThreadId}. 是否为线程池线程: {CurrentThread.IsThreadPoolThread}.); } 运行结果如下图所示。 1.3 使用任务执行基本的操作# 在本节中使用任务执行基本的操作并且获取任务执行完成后的结果值。本节内容比较简单在此不做过多介绍。 演示代码如下在主线程中要获取结果值常用的方式就是访问task.Result属性如果任务线程还没执行完毕那么会阻塞主线程直到线程执行完。如果任务线程执行完毕那么将直接拿到运算的结果值。 在Task 3中使用了task.Status来打印线程的状态线程每个状态的具体含义将在下一节中介绍。 Copy static void Main(string[] args) { // 直接执行方法 作为参照 TaskMethod(主线程任务); // 访问 Result属性 达到运行结果  Taskint 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 Taskint CreateTask(string name) { return new Taskint(() 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。 演示代码如下所示使用ContinueWith()和OnCompleted()方法组合了任务来运行搭配不同的TaskCreationOptions和TaskContinuationOptions来实现不同的效果。 Copy static void Main(string[] args) { WriteLine($主线程 线程 Id {CurrentThread.ManagedThreadId}); // 创建两个任务 var firstTask new Taskint(() TaskMethod(Frist Task,3)); var secondTask new Taskint(() TaskMethod(Second Task,2)); // 在默认的情况下 ContiueWith会在前面任务运行后再运行 firstTask.ContinueWith(t WriteLine($第一次运行答案是 {t.Result}. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程: {CurrentThread.IsThreadPoolThread})); // 启动任务 firstTask.Start(); secondTask.Start(); Sleep(TimeSpan.FromSeconds(4)); // 这里会紧接着 Second Task运行后运行 但是由于添加了 OnlyOnRanToCompletion 和 ExecuteSynchronously 所以会由运行SecondTask的线程来 运行这个任务 Task continuation secondTask.ContinueWith(t WriteLine($第二次运行的答案是 {t.Result}. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程{CurrentThread.IsThreadPoolThread}),TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously); // OnCompleted 是一个事件 当contiuation运行完成后 执行OnCompleted Action事件 continuation.GetAwaiter().OnCompleted(() WriteLine($后继任务完成. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程 {CurrentThread.IsThreadPoolThread})); Sleep(TimeSpan.FromSeconds(2)); WriteLine(); firstTask new Taskint(() { // 使用了TaskCreationOptions.AttachedToParent 将这个Task和父Task关联 当这个Task没有结束时 父Task 状态为 WaitingForChildrenToComplete var innerTask Task.Factory.StartNew(() TaskMethod(Second Task,5), TaskCreationOptions.AttachedToParent); innerTask.ContinueWith(t TaskMethod(Thrid Task, 2), TaskContinuationOptions.AttachedToParent); return TaskMethod(First Task,2); }); firstTask.Start(); // 检查firstTask线程状态 根据上面的分析 首先是 Running - WatingForChildrenToComplete - RanToCompletion while (! firstTask.IsCompleted) { WriteLine(firstTask.Status); Sleep(TimeSpan.FromSeconds(0.5)); } WriteLine(firstTask.Status); Console.ReadLine(); } static int TaskMethod(string name, int seconds) { WriteLine($任务 {name} 正在运行,线程池线程 Id {CurrentThread.ManagedThreadId},是否为线程池线程: {CurrentThread.IsThreadPoolThread}); Sleep(TimeSpan.FromSeconds(seconds)); return 42 * seconds; } 运行结果如下图所示与预期结果一致。其中使用了task.Status来打印任务运行的状态对于task.Status的状态具体含义如下表所示。 成员名称说明Canceled该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认此时该标记处于已发送信号状态或者在该任务开始执行之前已向该任务的 CancellationToken 发出了信号。 有关详细信息请参阅任务取消。Created该任务已初始化但尚未被计划。Faulted由于未处理异常的原因而完成的任务。RanToCompletion已成功完成执行的任务。Running该任务正在运行但尚未完成。WaitingForActivation该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。WaitingForChildrenToComplete该任务已完成执行正在隐式等待附加的子任务完成。WaitingToRun该任务已被计划执行但尚未开始执行。1.5 将APM模式转换为任务# 在前面的章节中介绍了基于IAsyncResult接口实现了BeginXXXX/EndXXXX方法的就叫APM模式。APM模式非常古老那么如何将它转换为TAP模式呢对于常见的几种APM模式异步任务我们一般选择使用Task.Factory.FromAsync()方法来实现将APM模式转换为TAP模式。 演示代码如下所示比较简单不作过多介绍。 Copy static void Main(string[] args) { int threadId; AsynchronousTask d Test; IncompatibleAsychronousTask e Test; // 使用 Task.Factory.FromAsync方法 转换为Task WriteLine(Option 1); Taskstring task Taskstring.Factory.FromAsync(d.BeginInvoke(异步任务线程, CallBack, 委托异步调用), d.EndInvoke); task.ContinueWith(t WriteLine($回调函数执行完毕现在运行续接函数结果{t.Result})); while (!task.IsCompleted) { WriteLine(task.Status); Sleep(TimeSpan.FromSeconds(0.5)); } WriteLine(task.Status); Sleep(TimeSpan.FromSeconds(1)); WriteLine(----------------------------------------------); WriteLine(); // 使用 Task.Factory.FromAsync重载方法 转换为Task WriteLine(Option 2); task Taskstring.Factory.FromAsync(d.BeginInvoke,d.EndInvoke,异步任务线程,委托异步调用); task.ContinueWith(t WriteLine($任务完成现在运行续接函数结果{t.Result})); while (!task.IsCompleted) { WriteLine(task.Status); Sleep(TimeSpan.FromSeconds(0.5)); } WriteLine(task.Status); Sleep(TimeSpan.FromSeconds(1)); WriteLine(----------------------------------------------); WriteLine(); // 同样可以使用 FromAsync方法 将 BeginInvoke 转换为 IAsyncResult 最后转换为 Task WriteLine(Option 3); IAsyncResult ar e.BeginInvoke(out threadId, CallBack, 委托异步调用); task Taskstring.Factory.FromAsync(ar, _ e.EndInvoke(out threadId, ar)); task.ContinueWith(t WriteLine($任务完成现在运行续接函数结果{t.Result}线程Id {threadId})); while (!task.IsCompleted) { WriteLine(task.Status); Sleep(TimeSpan.FromSeconds(0.5)); } WriteLine(task.Status); ReadLine(); } delegate string AsynchronousTask(string threadName); delegate string IncompatibleAsychronousTask(out int threadId); static void CallBack(IAsyncResult ar) { WriteLine(开始运行回调函数...); WriteLine($传递给回调函数的状态{ar.AsyncState}); WriteLine($是否为线程池线程{CurrentThread.IsThreadPoolThread}); WriteLine($线程池工作线程Id{CurrentThread.ManagedThreadId}); } static string Test(string threadName) { WriteLine(开始运行...); WriteLine($是否为线程池线程{CurrentThread.IsThreadPoolThread}); Sleep(TimeSpan.FromSeconds(2)); CurrentThread.Name threadName; return $线程名{CurrentThread.Name}; } static string Test(out int threadId) { WriteLine(开始运行...); WriteLine($是否为线程池线程{CurrentThread.IsThreadPoolThread}); Sleep(TimeSpan.FromSeconds(2)); threadId CurrentThread.ManagedThreadId; return $线程池线程工作Id是{threadId}; } 运行结果如下图所示。 1.6 将EAP模式转换为任务# 在上几章中有提到通过BackgroundWorker类通过事件的方式实现的异步我们叫它EAP模式。那么如何将EAP模式转换为任务呢很简单我们只需要通过TaskCompletionSource类即可将EAP模式转换为任务。 演示代码如下所示。 Copy static void Main(string[] args) { var tcs new TaskCompletionSourceint(); var worker new BackgroundWorker(); worker.DoWork (sender, eventArgs) { eventArgs.Result TaskMethod(后台工作, 5); }; // 通过此方法 将EAP模式转换为 任务 worker.RunWorkerCompleted (sender, eventArgs) { if (eventArgs.Error ! null) { tcs.SetException(eventArgs.Error); } else if (eventArgs.Cancelled) { tcs.SetCanceled(); } else { tcs.SetResult((int)eventArgs.Result); } }; worker.RunWorkerAsync(); // 调用结果 int result tcs.Task.Result; WriteLine($结果是{result}); ReadLine(); } static int TaskMethod(string name, int seconds) { WriteLine($任务{name}运行在线程{CurrentThread.ManagedThreadId}上. 是否为线程池线程{CurrentThread.IsThreadPoolThread}); Sleep(TimeSpan.FromSeconds(seconds)); return 42 * seconds; } 运行结果如下图所示。 1.7 实现取消选项# 在TAP模式中实现取消选项和之前的异步模式一样都是使用CancellationToken来实现但是不同的是Task构造函数允许传入一个CancellationToken从而在任务实际启动之前取消它。 演示代码如下所示。 Copy static void Main(string[] args) { var cts new CancellationTokenSource(); // new Task时 可以传入一个 CancellationToken对象 可以在线程创建时 变取消任务 var longTask new Taskint(() TaskMethod(Task 1, 10, cts.Token), cts.Token); WriteLine(longTask.Status); cts.Cancel(); WriteLine(longTask.Status); WriteLine(第一个任务在运行前被取消.); // 同样的 可以通过CancellationToken对象 取消正在运行的任务 cts new CancellationTokenSource(); longTask new Taskint(() TaskMethod(Task 2, 10, cts.Token), cts.Token); longTask.Start(); for (int i 0; i 5; i) { Sleep(TimeSpan.FromSeconds(0.5)); WriteLine(longTask.Status); } cts.Cancel(); for (int i 0; i 5; i) { Sleep(TimeSpan.FromSeconds(0.5)); WriteLine(longTask.Status); } WriteLine($这个任务已完成结果为{longTask.Result}); ReadLine(); } static int TaskMethod(string name, int seconds, CancellationToken token) { WriteLine($任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程{CurrentThread.IsThreadPoolThread}); for (int i 0; i seconds; i) { Sleep(TimeSpan.FromSeconds(1)); if (token.IsCancellationRequested) { return -1; } } return 42 * seconds; } 运行结果如下图所示这里需要注意的是如果是在任务执行之前取消了任务那么它的最终状态是Canceled。如果是在执行过程中取消任务那么它的状态是RanCompletion。 1.8 处理任务中的异常# 在任务中处理异常和其它异步方式处理异常类似如果能在所发生异常的线程中处理那么不要在其它地方处理。但是对于一些不可预料的异常那么可以通过几种方式来处理。 可以通过访问task.Result属性来处理异常因为访问这个属性的Get方法会使当前线程等待直到该任务完成并将异常传播给当前线程这样就可以通过try catch语句块来捕获异常。另外使用task.GetAwaiter().GetResult()方法和第使用task.Result类似同样可以捕获异常。如果是要捕获多个任务中的异常错误那么可以通过ContinueWith()方法来处理。 具体如何实现演示代码如下所示。 Copy static void Main(string[] args) { Taskint task; // 在主线程中调用 task.Result task中的异常信息会直接抛出到 主线程中 try { task Task.Run(() TaskMethod(Task 1, 2)); int result task.Result; WriteLine($结果为: {result}); } catch (Exception ex) { WriteLine($异常被捕捉{ex.Message}); } WriteLine(------------------------------------------------); WriteLine(); // 同上 只是访问Result的方式不同 try { task Task.Run(() TaskMethod(Task 2, 2)); int result task.GetAwaiter().GetResult(); WriteLine($结果为{result}); } catch (Exception ex) { WriteLine($异常被捕捉: {ex.Message}); } WriteLine(----------------------------------------------); WriteLine(); var t1 new Taskint(() TaskMethod(Task 3, 3)); var t2 new Taskint(() TaskMethod(Task 4, 4)); var complexTask Task.WhenAll(t1, t2); // 通过ContinueWith TaskContinuationOptions.OnlyOnFaulted的方式 如果task出现异常 那么才会执行该方法 var exceptionHandler complexTask.ContinueWith(t { WriteLine($异常被捕捉{t.Exception.Message}); foreach (var ex in t.Exception.InnerExceptions) { WriteLine($-------------------------- {ex.Message}); } },TaskContinuationOptions.OnlyOnFaulted); t1.Start(); t2.Start(); ReadLine(); } static int TaskMethod(string name, int seconds) { WriteLine($任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程{CurrentThread.IsThreadPoolThread}); Sleep(TimeSpan.FromSeconds(seconds)); // 人为抛出一个异常 throw new Exception(Boom!); return 42 * seconds; } 运行结果如下所示需要注意的是如果在ContinueWith()方法中捕获多个任务产生的异常那么它的异常类型是AggregateException具体的异常信息包含在InnerExceptions里面要注意和InnerException区分。 1.9 并行运行任务# 本节中主要介绍了两个方法的使用一个是等待组中全部任务都执行结束的Task.WhenAll()方法另一个是只要组中一个方法执行结束都执行的Task.WhenAny()方法。 具体使用如下演示代码所示。 Copy static void Main(string[] args) { // 第一种方式 通过Task.WhenAll 等待所有任务运行完成 var firstTask new Taskint(() TaskMethod(First Task, 3)); var secondTask new Taskint(() TaskMethod(Second Task, 2)); // 当firstTask 和 secondTask 运行完成后 才执行 whenAllTask的ContinueWith var whenAllTask Task.WhenAll(firstTask, secondTask); whenAllTask.ContinueWith(t WriteLine($第一个任务答案为{t.Result[0]}第二个任务答案为{t.Result[1]}), TaskContinuationOptions.OnlyOnRanToCompletion); firstTask.Start(); secondTask.Start(); Sleep(TimeSpan.FromSeconds(4)); // 使用WhenAny方法 只要列表中有一个任务完成 那么该方法就会取出那个完成的任务 var tasks new ListTaskint(); for (int i 0; i 4; i) { int counter 1; var task new Taskint(() TaskMethod($Task {counter},counter)); tasks.Add(task); task.Start(); } while (tasks.Count 0) { var completedTask Task.WhenAny(tasks).Result; tasks.Remove(completedTask); WriteLine($一个任务已经完成结果为 {completedTask.Result}); } ReadLine(); } static int TaskMethod(string name, int seconds) { WriteLine($任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程{CurrentThread.IsThreadPoolThread}); Sleep(TimeSpan.FromSeconds(seconds)); return 42 * seconds; } 运行结果如下图所示。 1.10 使用TaskScheduler配置任务执行# 在Task中负责任务调度是TaskScheduler对象FCL提供了两个派生自TaskScheduler的类型线程池任务调度器(Thread Pool Task Scheduler)和同步上下文任务调度器(Synchronization Scheduler)。默认情况下所有应用程序都使用线程池任务调度器但是在UI组件中不使用线程池中的线程避免跨线程更新UI需要使用同步上下文任务调度器。可以通过执行TaskScheduler的FromCurrentSynchronizationContext()静态方法来获得对同步上下文任务调度器的引用。 演示程序如下所示为了延时同步上下文任务调度器我们此次使用WPF来创建项目。 MainWindow.xaml 代码如下所示。 Copy Window x:ClassRecipe9.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:dhttp://schemas.microsoft.com/expression/blend/2008 xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006 xmlns:localclr-namespace:Recipe9 mc:Ignorabled TitleMainWindow Height450 Width800 Grid TextBlock NameContentTextBlock HorizontalAlignmentLeft Margin44,134,0,0 VerticalAlignmentTop Width425 Height40/ Button ContentSync HorizontalAlignmentLeft Margin45,190,0,0 VerticalAlignmentTop Width75 ClickButtonSync_Click/ Button ContentAsync HorizontalAlignmentLeft Margin165,190,0,0 VerticalAlignmentTop Width75 ClickButtonAsync_Click/ Button ContentAsync OK HorizontalAlignmentLeft Margin285,190,0,0 VerticalAlignmentTop Width75 ClickButtonAsyncOK_Click/ /Grid /Window MainWindow.xaml.cs 代码如下所示。 Copy /// summary /// MainWindow.xaml 的交互逻辑 /// /summary public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } // 同步执行 计算密集任务 导致UI线程阻塞 private void ButtonSync_Click(object sender, RoutedEventArgs e) { ContentTextBlock.Text string.Empty; try { string result TaskMethod().Result; ContentTextBlock.Text result; } catch (Exception ex) { ContentTextBlock.Text ex.InnerException.Message; } } // 异步的方式来执行 计算密集任务 UI线程不会阻塞 但是 不能跨线程更新UI 所以会有异常 private void ButtonAsync_Click(object sender, RoutedEventArgs e) { ContentTextBlock.Text string.Empty; Mouse.OverrideCursor Cursors.Wait; Taskstring task TaskMethod(); task.ContinueWith(t { ContentTextBlock.Text t.Exception.InnerException.Message; Mouse.OverrideCursor null; }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext()); } // 通过 异步 和 FromCurrentSynchronizationContext方法 创建了线程同步的上下文 没有跨线程更新UI private void ButtonAsyncOK_Click(object sender, RoutedEventArgs e) { ContentTextBlock.Text string.Empty; Mouse.OverrideCursor Cursors.Wait; Taskstring task TaskMethod(TaskScheduler.FromCurrentSynchronizationContext()); task.ContinueWith(t Mouse.OverrideCursor null, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); } Taskstring TaskMethod() { return TaskMethod(TaskScheduler.Default); } Taskstring TaskMethod(TaskScheduler scheduler) { Task delay Task.Delay(TimeSpan.FromSeconds(5)); return delay.ContinueWith(t { string str $任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程{CurrentThread.IsThreadPoolThread}; Console.WriteLine(str); ContentTextBlock.Text str; return str; }, scheduler); } } 运行结果如下所示从左至右依次单击按钮前两个按钮将会引发异常。 具体信息如下所示。 参考书籍 本文主要参考了以下几本书在此对这些作者表示由衷的感谢感谢你们为.Net的发扬光大所做的贡献 《CLR via C#》《C# in Depth Third Edition》《Essential C# 6.0》《Multithreading with C# Cookbook Second Edition》《C#多线程编程实战》源码下载点击链接 示例源码下载 笔者水平有限如果错误欢迎各位批评指正 本来想趁待业期间的时间读完《Multithreading with C# Cookbook Second Edition》这本书并且分享做的相关笔记但是由于笔者目前职业规划和身体原因可能最近都没有时间来更新这个系列没法做到几天一更。请大家多多谅解但是笔者一定会将这个系列全部更新完成的感谢大家的支持 作者InCerry 出处https://www.cnblogs.com/InCerry/p/9450493.html 版权本文采用「署名 4.0 国际」知识共享许可协议进行许可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/922939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站定位的核心意义下载wordpress低版本

后端:python 前端:vue.jselementui 框架:django/flask Python版本:python3.7 数据库:mysql 数据库工具:Navicat 开发软件:PyCharm 本系统在设计过程中,很好地发挥了该开发方式的优…

网站域名跳转代码潍坊网站制作保定公司电话

当下,社会生活的节奏非常快,人们忙于工作,在日常生活家务清洁中面临着时间、精力不足的问题,因此对家政服务的需求日益增加,这也推动了家政行业的迅速发展。目前不少年轻人都开始涌入到了家政行业中,市场的…

2025阳台装修品牌推荐榜:优质阳台厂商资质、技术、服务测评及高口碑企业优选指南,浙江多为建筑服务与性价比兼具!

阳台已从单一的功能区升级为家居生活的 “第三空间”,承载着休闲、收纳、观景等多重需求,但行业乱象仍让消费者决策困难。部分企业缺乏资质导致施工粗糙,漏水、材质霉变等问题频发;服务断层现象普遍,测量、设计到…

2025 年杭州小程序开发机构最新推荐榜单:覆盖多行业定制需求,助力企业精准选靠谱服务商

在数字化转型加速推进的 2025 年,小程序已成为企业连接用户、拓展业务的关键载体,然而杭州小程序开发市场却鱼龙混杂,让企业选型陷入困境。不少企业因选错服务商,遭遇系统拓展性差、项目延期、功能与需求脱节等问题…

2025年杭州软件开发公司最新品牌推荐榜:聚焦技术实力与售后体系的优质服务商精选指南!

在数字经济高速发展的当下,杭州软件开发市场规模持续扩大,各类服务商数量激增,企业在选择合作方时常常陷入困境。部分服务商技术储备不足,难以应对复杂项目开发需求;沟通效率低下,导致需求理解偏差,项目交付质量…

python可以做网站前台么民房做酒店出租网站app

离开北京的时候是周五,天气很诡异:潮湿而又充满尘燥,阴霾而又阳光明亮, 真不知道这样的天气怎么能够共存?! 我发神经的做上了飞往云南的航班,这是休息,是治疗。 飞机正位&#xff0c…

湖南省茶陵一中校庆120周年:205班捐款

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087各位同学: 大家好! 在母校茶陵一中百廿年校庆之际,我们205班为母…

实用指南:计算机网络-ipv4首部校验原理

实用指南:计算机网络-ipv4首部校验原理pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mo…

网站建设ppt答辩wordpress 自动采集插件

矩阵的特征分解 特征值和特征向量的定义 抄来的:奇异值分解 困惑1:特征值和特征向量,和原矩阵是怎样的关系,需要一个栗子进行更具象的认识 困惑2:为什么多个特征向量组合成的矩阵,可以构成矩阵A的特征分解…

宁波企业自助建站wordpress多边形按钮

计算机网络-2019 王道考研 计算机网络-1.2.4TCP,IP参考模型和五层参考模型 文章目录4.TCP,IP参考模型和五层参考模型4.1OSI参考模型与TCP/IP参考模型4.2OSI参考模型与TCP/IP参考模型的相同点4.3OSI参考模型与TCP/IP参考模型的不同点4.4五层参考协议4.4五…

2025 年人工智能培训厂家最新推荐排行榜:聚焦人工智能培训合规运营机构、产业适配能力与教学实力深度解析

当前人工智能培训行业虽发展迅猛,但乱象丛生,给学员选择带来极大困扰。部分机构缺乏合规资质,教学质量无保障,学员权益易受损;多数机构课程体系与产业需求脱节,内容更新滞后于技术趋势,导致学员所学技能无法满足…

一种以125kHz低频识别 + 2.4GHz数据传输”的方案,通过频率优势互补,为近距离物联网应用提供了可靠、精准且高效的解决方案

​ 在要求精准触发的近距离场景,传统的解决方案有时力不从心。我们测试了融合125kHz低频唤醒与2.4GHz高频传输的“近距离低频识别解决方案”,正以其独特优势,在智能打卡、运动计时、汽车钥匙等领域开启新可能。 许多…

深入解析:STM32H743-ARM例程9-IWDG看门狗

深入解析:STM32H743-ARM例程9-IWDG看门狗pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "…

高效做PPT!5个亲测模板网站,10分钟出专业演示 !

“明天就要交汇报 PPT,现在还没动笔”“花 3 小时找的模板,打开全是水印”“数据图表调了半天,还是歪歪扭扭”—— 这是不是你的职场日常?作为每天和 PPT 打交道的运营人,我曾因一套季度总结 PPT 熬到凌晨两点,直…

网站最新程序策划书推荐个好看的网站

目录 0 专栏介绍1 时序差分强化学习2 策略评估原理3 策略改进原理3.1 SARSA算法3.2 Q-Learning算法 0 专栏介绍 本专栏重点介绍强化学习技术的数学原理,并且采用Pytorch框架对常见的强化学习算法、案例进行实现,帮助读者理解并快速上手开发。同时&#…

【WCH蓝牙系列芯片】-基于CH592开发板——HID_Keyboard中添加读、写、通知的服务属性

【WCH蓝牙系列芯片】-基于CH592开发板——HID_Keyboard中添加读、写、通知的服务属性--------------------------------------------------------------------------------------------------------------------------…

2025 年 AI 健康管理厂商最新推荐榜单:覆盖多场景需求,深护智康等优质品牌助力行业升级

随着 “AI + 健康管理” 模式在医疗、公卫、药店、母婴等多场景的深度渗透,市场对专业厂商的需求愈发迫切。当前行业面临传统系统定制成本高、服务效率低、细分场景支持不足等痛点,众多机构难以快速找到适配自身需求…

虚幻5.6插件添加自定义shader

主要是对官方文档的一个补充:虚幻引擎插件中的 Shader | 虚幻引擎 5.6 文档 | Epic Developer Community 添加虚拟源文件路径查看代码 void FYKComputeModule::StartupModule() {// 查找插件TSharedPtr<IPlugin>…

建设银行网站电脑版wordpress discuz

题目描述&#xff1a;给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 题目链接&#xff1a;LeetCode-47-全排列Ⅱ 解题思路&#xff1a;注意题目中给的是包含重复数字&#xff0c;所以需要去重操作&#xff1b; 这道题不需要 startIndex&…

勒索软件速度危机:AI驱动下的网络安全新挑战

勒索软件攻击速度已从2019年的9天缩短至2025年的25分钟,增长100倍。AI技术正被用于网络犯罪,82.6%的网络钓鱼邮件采用AI技术。防御需依赖AI检测、自动化响应和XDR平台构建速度兼容的防御体系。勒索软件速度危机 当网…