(译)构建Async同步基元,Part 6 AsyncLock
最近在学习.NET4.5关于“并行任务”的使用。“并行任务”有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄、信号量、lock、ReaderWriterLock……等同步基元对象,但我们可以沿溪这一编程习惯,那么这系列翻译就是给“并行任务”封装同步基元对象。翻译资源来源《(译)关于Async与Await的FAQ》
1. 构建Async同步基元,Part 1 AsyncManualResetEvent
2. 构建Async同步基元,Part 2 AsyncAutoResetEvent
3. 构建Async同步基元,Part 3 AsyncCountdownEvent
4. 构建Async同步基元,Part 4 AsyncBarrier
5. 构建Async同步基元,Part 5 AsyncSemaphore
6. 构建Async同步基元,Part 6 AsyncLock
7. 构建Async同步基元,Part 7 AsyncReaderWriterLock
开始:构建Async同步基元,Part 6 AsyncLock
最近,我们刚刚构建了一个AsyncSemphore,现在,我们来构建一个在using块中支持异步互斥机制的类型。
正如前面帖子所述,信号量非常适用于限流及资源访问管理。你可以给信号量一个初始计数,然后它将只允许指定数量的消费者成功获得信号,强迫多余的请求等待直到资源被释放内部信号计数大于0。信号量可以用来保护正确进入特定代码区域,并且内部计数可以设置为1。通过这种方式,你能使用信号量来达到互斥目的,比如:
private readonly AsyncSemaphore m_lock = new AsyncSemaphore(1); … await m_lock.WaitAsync(); try { … // protected code here } finally { m_lock.Release(); }
我们可以通过创建一个AsyncLock类型来支持使用using关键字来简化编码。我们的目的是和上面片段一致的,但是通过类似下面的代码来实现:
private readonly AsyncLock m_lock = new AsyncLock(); … using(var releaser = await m_lock.LockAsync()) { … // protected code here }
为了达到这样的效果,我们将构建下面这样的类型:
public class AsyncLock { public AsyncLock(); public Task<Releaser> LockAsync(); public struct Releaser : IDisposable { public void Dispose(); } }
在内部,我们将维护两个成员。我们使用AsyncSemaphore对象来处理大部分逻辑,使用m_releaser变量缓存的Task<Releaser>实例,当访问的锁没有竞争时,可以直接返回从而避免不必要的分配。
private readonly AsyncSemaphore m_semaphore; private readonly Task<Releaser> m_releaser;
Releaser结构仅仅是实现了IDisposable接口的Dispose()方法,该方法将释放底层的信号量。这是允许我们在using块中使用Releaser构造的原因,由using自动生成的finally块将调用Dispose()方法,即Seamphore实例的Release()方法来进行释放。
public struct Releaser : IDisposable { private readonly AsyncLock m_toRelease; internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; } public void Dispose() { if (m_toRelease != null) m_toRelease.m_semaphore.Release(); } }
我们的AsyncLock的构造函数仅仅是初始化成员,使用数值为1的参数创建一个Semaphore,并且创建一个缓存的Task<Releaser>。
public AsyncLock() { m_semaphore = new AsyncSemaphore(1); m_releaser = Task.FromResult(new Releaser(this)); }
最后,构建我们的LockAsync方法。我们首先调用Semaphore的WaitAsync()获得一个Task,用于指代我们获得的锁。如果Task已经完成,则同步返回缓存的Task<Releaser>,这也意味着如果锁没有竞争就不需要分配。如果Task没有完成,则返回一个Task<Releaser>的延续任务。
public Task<Releaser> LockAsync() { var wait = m_semaphore.WaitAsync(); return wait.IsCompleted ? m_releaser : wait.ContinueWith((_,state) => new Releaser((AsyncLock)state), this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); }
这就是本节要讲的AsyncLock。
完整源码如下:
public class AsyncLock { private readonly AsyncSemaphore m_semaphore; // 缓存Task<Releaser>实例,当访问的锁没有竞争时,可以直接返回从而避免不必要的分配。 private readonly Task<Releaser> m_releaser; public AsyncLock() { // 信号量为1,用于实现互斥 m_semaphore = new AsyncSemaphore(1); m_releaser = Task.FromResult(new Releaser(this)); } public Task<Releaser> LockAsync() { var wait = m_semaphore.WaitAsync(); return wait.IsCompleted ? m_releaser : wait.ContinueWith((_, state) => new Releaser((AsyncLock)state) , this, CancellationToken.None , TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } public struct Releaser : IDisposable { private readonly AsyncLock m_toRelease; internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; } // using块生成的finally块调用IDisposable接口的Dispose()方法 public void Dispose() { if (m_toRelease != null) m_toRelease.m_semaphore.Release(); } } } /// <summary> /// 使用方式 /// </summary> public class AsyncLock_Test { private readonly AsyncLock m_lock = new AsyncLock(); public async void Test() { using (var releaser = await m_lock.LockAsync()) { // 限制访问代码 } } }
下一节,我将实现一个异步版本的 read/writer 锁。
推荐阅读:
感谢你的观看……
原文:《Building Async Coordination Primitives, Part 6: AsyncLock》
作者:滴答的雨
出处:http://www.cnblogs.com/heyuquan/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
欢迎园友讨论下自己的见解,及向我推荐更好的资料。
本文如对您有帮助,还请多帮 【推荐】 下此文。
谢谢!!! (*^_^*)
技术群:(339322839广西IT技术交流),欢迎你的加入