【发布时间】:2014-01-26 19:04:01
【问题描述】:
我的团队正在 C# 5.0 中使用 async/await 开发一个多线程应用程序。在实现线程同步的过程中,经过多次迭代,我们想出了一个(可能是新颖的?)带有内部锁的新 SynchronizationContext 实现:
- 调用 Post 时:
- 如果可以获取锁,则立即执行委托
- 如果无法获得锁,则委托排队
- 调用发送时:
- 如果可以获取锁,则执行委托
- 如果无法获得锁,则线程被阻塞
在所有情况下,在执行委托之前,上下文将自己设置为当前上下文,并在委托返回时恢复原始上下文。
这是一个不寻常的模式,因为我们显然不是第一个编写这样一个应用程序的人,我想知道:
- 这种模式真的安全吗?
- 有没有更好的方法来实现线程同步?
这是 SerializingSynchronizationContext 的源代码和GitHub 上的演示。
它的使用方法如下:
- 每个需要保护的类都会创建自己的上下文实例,例如互斥体。
-
上下文是可等待的,因此以下语句是可能的。
await myContext;这只会导致方法的其余部分在上下文的保护下运行。
- 类的所有方法和属性都使用这种模式来保护数据。在等待之间,一次只能在上下文上运行一个线程,因此状态将保持一致。当到达等待时,允许下一个计划线程在上下文上运行。
- 如果需要保持原子性,即使是等待的表达式,自定义 SynchronizationContext 也可以与 AsyncLock 结合使用。
- 类的同步方法也可以使用自定义上下文进行保护。
【问题讨论】:
-
这是重新发明了 TPL DataFlow 的
ActionBlock一点吗? -
@Andrew 不是真的;它不一定主要用于受 CPU 限制的工作,而这正是 TPL 数据流的用途。它本质上是一种复制 UI 同步上下文的方式。
-
@Servy 是的。仅使用单个线程完成所有工作的想法似乎反映了这一点。
-
@Servy 我不反对...
-
@Noseratio 此自定义上下文没有线程关联。它一次只允许一个线程执行受上下文保护的任何代码,因此它更像 ASP.NET。更关心的是如何使用这个上下文(参见演示代码),即像一种监视器锁,在等待之前和之后自动释放和重新获取锁。对象具有状态并且可能被多个线程接触,因此不安全。目标主要是易于使用和应用的模式。
标签: c# .net async-await synchronizationcontext