.NET 异步与并发
文章平均质量分 88
什么代码扯上了多线程都会变得复杂一些。.NET 中的 async/await 出现以后这种情况好了很多。本专栏解读 async/await 的原理,解读 .NET 中的竞争、饥饿和死锁问题。
walter lv
这个作者很懒,什么都没留下…
展开
-
.NET 中使用 Mutex 进行跨越进程边界的同步
Mutex 是 Mutual Exclusion 的缩写,是互斥锁,用于防止两个线程同时对计算机上的同一个资源进行访问。不过相比于其他互斥的方式,Mutex 能够跨越线程边界。本文内容Mutex 是什么?简单的 Mutex(不能跨进程互斥)创建跨进程互斥的 Mutex处理异常情况ApplicationExceptionAbandonedMutexException参考资料Mutex 是什么?...原创 2019-01-27 13:39:29 · 669 阅读 · 0 评论 -
.NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
林德熙 小伙伴希望保存一个文件,并且希望如果出错了也要不断地重试。然而我认为如果一直错误则应该对外抛出异常让调用者知道为什么会一直错误。这似乎是一个矛盾的要求。然而最终我想到了一个办法:让重试一直进行下去,谁需要关心异常谁就去 catch 异常,不需要关心异常的模块则跟着一直重试直到成功。我们通过编写一个自己的 Awaiter 来实现,本文将说明其思路和最终实现的代码。本文内容Awaite...原创 2019-01-01 10:57:58 · 637 阅读 · 0 评论 -
.NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
你可以使用临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphores)和事件(Event)来处理线程同步。然而,在编写一些异步处理函数,尤其是还有 async 和 await 使用的时候,还有一些更方便的类型可以用来处理线程同步。使用 TaskCompletionSource,你可以轻松地编写既可以异步等待,又可以同步等待的代码来。本文内容等待事件引发事件...原创 2019-01-01 10:55:45 · 1154 阅读 · 0 评论 -
.NET 中什么样的类是可使用 await 异步等待的?
我们已经知道 Task 是可等待的,但是去看看 Task 类的实现,几乎找不到哪个基类、接口或者方法属性能够告诉我们与 await 相关。而本文将探索什么样的类是可使用 await 异步等待的?Dixin’s Blog - Understanding C# async / await (2) The Awaitable-Awaiter Pattern 一文解决了我们的疑惑。async/awa...原创 2018-12-23 15:15:37 · 897 阅读 · 0 评论 -
.NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
.NET 中的 async / await 写异步代码用起来真的很爽,就像写同步一样。我们可以在各种各样的异步代码中看到 Task 返回值,这样大家便可以使用 await 等待这个方法。不过,有时需要写一些特别的异步方法,这时需要自己来实现一个可以异步等待的对象。本文将讲述如何实现一个可等待对象,一个自定义的 Awaiter。本文内容Awaiter 系列文章可等待对象编写基本的 Awaite...原创 2018-12-23 15:12:36 · 811 阅读 · 0 评论 -
定义一组抽象的 Awaiter 的实现接口,你下次写自己的 await 可等待对象时将更加方便
我在几篇文章中都说到了在 .NET 中自己实现 Awaiter 情况。async / await 写异步代码用起来真的很爽,就像写同步一样。然而实现 Awaiter 没有现成的接口,它需要你按照编译器的要求为你的类型添加一些具有特定名称的属性和方法。然而没有接口的帮助,我们编写起来就很难获得工具(如 ReSharper)自动生成代码的支持。本文将分享我提取的自己实现 Awaiter 的接口。你只...原创 2018-12-23 15:10:54 · 473 阅读 · 0 评论 -
不要使用 Dispatcher.Invoke,因为它可能在你的延迟初始化 Lazy<T> 中导致死锁
WPF 中为了 UI 的跨线程访问,提供了 Dispatcher 线程模型。其 Invoke 方法,无论在哪个线程调用,都可以让传入的方法回到 UI 线程。然而,如果你在 Lazy 上下文中使用了 Invoke,那么当这个 Lazy&lt;T&gt; 跨线程并发时,极有可能导致死锁。本文将具体说说这个例子。本文内容一段死锁的代码此死锁的触发条件此死锁的原因此死锁的解决方法更多死锁问题一段死...原创 2018-12-23 14:58:39 · 2653 阅读 · 0 评论 -
在有 UI 线程参与的同步锁(如 AutoResetEvent)内部使用 await 可能导致死锁
AutoResetEvent、ManualResetEvent、Monitor、lock 等等这些用来做同步的类,如果在异步上下文(await)中使用,需要非常谨慎。本文将说一个在同步上下文中非常常见的一种用法,换成异步上下文中会产生死锁的问题。本文内容一段正常的同步上下文的代码一个微调即会死锁此死锁的触发条件此死锁的原因更多死锁问题一段正常的同步上下文的代码先看看一段非常简单的代码:...原创 2018-12-23 14:58:02 · 509 阅读 · 0 评论 -
.NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
一个简单的 Task 不会消耗多少时间,但如果你不合适地将 Task 转为同步等待,那么也可能很快耗尽线程池的所有资源,出现类似死锁的情况。本文将以一个最简单的例子说明如何出现以及避免这样的问题。本文内容耗时的 Task.Run最简复现代码原因解决更多死锁问题耗时的 Task.Run谁都不会认为 Task.Run(() =&gt; 1) 这个异步任务执行会消耗多少时间。但实际上,如果你...原创 2018-12-23 14:57:05 · 2412 阅读 · 5 评论 -
了解 .NET 的默认 TaskScheduler 和线程池(ThreadPool)设置,避免让 Task.Run 的性能急剧降低
.NET Framework 4.5 开始引入 Task.Run,它可以很方便的帮助我们使用 async / await 语法,同时还使用线程池来帮助我们管理线程。以至于我们编写异步代码可以像编写同步代码一样方便。不过,如果滥用,也可能导致应用的性能急剧下降。本文将说明在默认线程池配置(ThreadPoolTaskScheduler)的情况下,应该如何使用 Task.Run 来避免性能的急剧降低...原创 2018-12-23 14:55:34 · 5246 阅读 · 0 评论 -
WPF 多线程 UI:设计一个异步加载 UI 的容器
你做 .NET 开发的时候,一定用过 DllImport 这个特性吧,这货是用于 P/Invoke (Platform Invoke, 平台调用) 的。这种 DllImport 标记的方法都带有一个 extern 关键字。那么有没有可能我们自己写一个自己的 extern 方法呢?答案是可以的。本文就写一个这样的例子。本文内容DllImport自定义的 extern让自定义的 extern 工...原创 2018-09-26 21:03:32 · 1523 阅读 · 0 评论 -
在编写异步方法时,使用 ConfigureAwait(false) 避免使用者死锁
我在 使用 Task.Wait()?立刻死锁(deadlock) 一文中站在类库使用者的角度看 async/await 代码的死锁问题;而本文将站在类库设计者的角度来看死锁问题。阅读本文,我们将知道如何编写类库代码,来尽可能避免类库使用者出现那篇博客中描述的死锁问题。可能死锁的代码现在,我们是类库设计者的身份,我们试图编写一个 RunAsync 方法用以异步执行某些操作。...原创 2018-03-23 22:00:36 · 14450 阅读 · 18 评论 -
将 async/await 异步代码转换为安全的不会死锁的同步代码
在 async/await 异步模型(即 TAP Task-based Asynchronous Pattern)出现以前,有大量的同步代码存在于代码库中,以至于这些代码全部迁移到 async/await 可能有些困难。这里就免不了将一部分异步代码修改为同步代码。然而传统的迁移方式存在或多或少的问题。本文将总结这些传统方法的坑,并推出一款异步转同步的新方法,解决传统方法的这些坑。背...原创 2018-03-19 19:46:33 · 1863 阅读 · 0 评论 -
.NET 中的轻量级线程安全
对线程安全有要求的代码中,通常会使用锁(lock)。自 .NET 诞生以来就有锁,然而从 .NET Framework 4.0 开始,又诞生了 6 个轻量级的线程安全方案:SpinLock, SpinWait, CountdownEvent, SemaphoreSlim, ManualResetEventSlim, Barrier。SpinLock, SpinWaitSpinLoc原创 2018-01-30 08:32:50 · 849 阅读 · 0 评论 -
异步任务中的重新进入(Reentrancy)
一个按钮,点击执行一个任务。我们可能直接在它的 Click 事件中写下了执行任务的代码。一般我们无需担心这样的代码会出现什么问题——但是,这样的好事情只对同步任务有效;一旦进入了异步世界,这便是无尽的 BUG!原创 2017-12-30 14:29:52 · 511 阅读 · 0 评论 -
如何实现一个可以用 await 异步等待的 Awaiter
.NET 和 C# 共同给我们带来的 async/await 异步编程模型(TAP)用起来真的很爽。为了实现异步等待,我们只需要在一切能够能够异步等待的方法前面加上 await 即可。能够异步等待的最常见的类型莫过于 Task,但也有一些其他类型。即便有些耗时操作没有返回可等待的类型,我们也可以用一句 Task.Run(action) 来包装(同步转异步 - 林德熙 中也有说明);不过副作用就是 R原创 2017-10-29 20:41:48 · 11283 阅读 · 14 评论 -
使用 Task.Wait()?立刻死锁(deadlock)
最近读到一篇异步转同步的文章,发现其中没有考虑到异步转同步过程中发生的死锁问题,所以特地在本文说说异步转同步过程中的死锁问题。文章作者 林德熙 已经修复了描述: - win10 uwp 异步转同步什么情况下会产生死锁?调用 Task.Wait() 或者 Task.Result 立刻产生死锁的充分条件: 1. 调用 Wait() 或 Result 的代码位于 UI 线程;...原创 2017-10-27 23:59:35 · 11014 阅读 · 7 评论 -
使用 ExceptionDispatchInfo 捕捉并重新抛出异常
当你跑起了一个异步线程,并用 await 异步等待时,有没有好奇为什么能够在主线程 catch 到异步线程的异常?当你希望在代码中提前收集好异常,最后一并把收集到的异常抛出的时候,能不能做到就像在原始异常发生的地方抛出一样?本文介绍 ExceptionDispatchInfo,专门用于重新抛出异常。它在 .NET Framework 4.5 中首次引入,并原生在 .NET Core 和 .NET S原创 2017-10-23 22:33:03 · 2396 阅读 · 0 评论 -
如何防止后台线程抛出的异常让程序崩溃退出
如果你的程序抛了异常,你是怎么处理的呢?等待程序崩溃退出?还是进行补救?如果是做 UI 开发,很容易就找到 Dispatcher.UnhandledException 事件,然后在事件中进行补救。如果补救成功,可以设置 e.Handled = true 来阻止异常继续让程序崩溃退出。但是,如果是后台线程抛出了异常呢?并没有 Dispatcher 可以用。所以我们就束手就擒让程序自己退出吗?WPF 和原创 2017-10-21 16:32:06 · 3543 阅读 · 0 评论