传送门:异步编程系列目录……

最近在学习.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同步基元.rar

开始:Async同步基元,Part 1 AsyncManualResetEvent

基于任务异步模式(TAP)不仅仅是关于开始然后异步等待完成的异步操作,更概括的说,任务可以用来指代各种事件,使你能够等待任何事的条件发生。我们甚至可以使用任务来构建简单的同步基元,这些同步基元类似.NET原生提供的非任务版本,但是它们允许等待异步完成。

线程同步基元之一:事件等待句柄,它们存在于.NET Framework。ManualResetEvent 和AutoResetEvent和.NET 4为ManualResetEvent新增的优化版本ManualResetEventSlim。事件等待句柄就是一方等待另一方提供信号。比如ManualResetEvent,他会在调用Set()后保持信号直到显示调用Reset()。

TaskCompletionSource<TResult>本身基于SpinWait结构与Task的IsCompleted属性实现类似事件等待句柄,仅仅是缺少Reset()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 表示未绑定到委托的 System.Threading.Tasks.Task<TResult> 的制造者方,
// 并通过 Tasks.TaskCompletionSource<TResult>.Task属性提供对使用者方的访问。
public class TaskCompletionSource<TResult>
{
    public TaskCompletionSource();
 
    // 获取由此 Tasks.TaskCompletionSource<TResult> 创建的 Tasks.Task<TResult>。
    public Task<TResult> Task { get; }
 
    // 将基础 Tasks.Task<TResult> 转换为 Tasks.TaskStatus.Canceled状态。
    public void SetCanceled();
    public bool TrySetCanceled();
 
    // 将基础 Tasks.Task<TResult> 转换为 Tasks.TaskStatus.Faulted状态。
    public void SetException(Exception exception);
    public void SetException(IEnumerable<Exception> exceptions);
    public bool TrySetException(Exception exception);
    public bool TrySetException(IEnumerable<Exception> exceptions);
 
    // 尝试将基础 Tasks.Task<TResult> 转换为 TaskStatus.RanToCompletion状态。
    public bool TrySetResult(TResult result);
    ……       
}

TaskCompletionSource<TResult>开始于无信号,它指代的任务不能完成,因此,等待这个任务的“异步方法”也不能完成。(Try)Set*方法充当信号,将任务切换到完成状态,这样才能完成等待任务。因此我们可以很容易基于TaskCompletionSource<TResult>来构建一个AsyncManualResetEvent。它可以为我们提供缺失的Reset()能力。接下来我们构建此AsyncManualResetEvent。

这是我们将构建的目标类型:

1
2
3
4
5
6
public class AsyncManualResetEvent
{
    public Task WaitAsync();
    public void Set();
    public void Reset();
}

WaitAsync()和Set()方法非常简单,直接封装TaskCompletionSource<bool>实例成员,如下:

1
2
3
4
5
6
7
public class AsyncManualResetEvent
{
    private volatile TaskCompletionSource<bool> m_tcs = new TaskCompletionSource<bool>();
    public Task WaitAsync() { return m_tcs.Task; }
    public void Set() { m_tcs.TrySetResult(true); }
    
}

剩下的只有Reset()方法了。我们的目标是使随后调用的WaitAsync()中返回的Task无法完成。因为Task最终状态只有完成状态(即,正常完成、取消、异常),所以我们需要切换一个新的TaskCompletionSource<bool>实例。这样做,我们只需要确保如果多个线程同时调用Reset()、Set()和WaitAsync(), WaitAsync()不会返回孤立的Task(即,我们不希望一个线程调用WaitAsync()返回一个不能完成的Task(已经被Reset())后,另一个线程又在新的Task上调用Set())。为了达到此目的,我们将确保如果当前Task已经完成就切换一个新的Task,并且还确保这个切换操作的原子性。(当然,还有其他策略实现此目标,这仅仅是我选择的一个特定例子)

注意:关键字volatile和Interlocked类的使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class AsyncManualResetEvent
{
    private volatile TaskCompletionSource<bool> m_tcs = new TaskCompletionSource<bool>();
 
    public Task WaitAsync() { return m_tcs.Task; }
    public void Set() { m_tcs.TrySetResult(true); } 
    public void Reset()
    {
        while (true)
        {
            var tcs = m_tcs;
            if (!tcs.Task.IsCompleted ||
                Interlocked.CompareExchange(ref m_tcs, new TaskCompletionSource<bool>(), tcs) == tcs)
                return;
        }
    }
}

到此,我们的AsyncManualResetEvent已经完成。然而,还有一个重要的潜在行为要记住。在之前的文章中,我们谈论过延续任务和他们是如何同步执行,这意味着延续任务将作为任务完成的一部分执行,在同一个线程上同步完成任务。对于TaskCompletionSource<TResult>,这意味着同步延续任务将作为(Try)Set*方法的一部分执行,也就是说,在AsyncManualResetEvent例子中,延续任务将作为Set()方法的一部分执行。根据你的需求,如果你不希望这种事情发生,有一些替代的方法。一种方法是异步运行(Try)Set*方法,并使Set()调用阻塞,直到任务真真完成(只是任务本身,不包括任务的延续任务)。Eg:

1
2
3
4
5
6
7
public void Set()
{
    var tcs = m_tcs;
    Task.Factory.StartNew(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs
       , CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default);
    tcs.Task.Wait();
}

当然,还有其他可能的方法,如何实现取决于你的需求。

这就是本节要讲的AsyncManualResetEvent。

完整源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class AsyncManualResetEvent
{
    private volatile TaskCompletionSource<bool> m_tcs = new TaskCompletionSource<bool>();
 
    public Task WaitAsync() { return m_tcs.Task; }
 
    public void Set()
    {
        var tcs = m_tcs;
        Task.Factory.StartNew(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs
           , CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default);
        tcs.Task.Wait();
    }
 
    public void Reset()
    {
        while (true)
        {
            var tcs = m_tcs;
            // 短逻辑单元 确保如果当前Task已经完成就切换一个新的Task。
            if (!tcs.Task.IsCompleted ||
                Interlocked.CompareExchange(ref m_tcs, new TaskCompletionSource<bool>(), tcs) == tcs)
                return;
        }
    }
}

下一节,我将实现一个async版本的AutoResetEvent。

推荐阅读:

异步编程:同步基元对象(上)

异步编程:同步基元对象(下)

感谢你的观看……

原文:《Building Async Coordination Primitives, Part 1: AsyncManualResetEvent》

作者:Stephen Toub – MSFT

作者:滴答的雨
出处:http://www.cnblogs.com/heyuquan/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

【转】3.1(译)构建Async同步基元,Part 1 AsyncManualResetEvent相关推荐

  1. 【转】3.7(译)构建Async同步基元,Part 7 AsyncReaderWriterLock

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  2. 【转】3.6(译)构建Async同步基元,Part 6 AsyncLock

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  3. 【转】3.5(译)构建Async同步基元,Part 5 AsyncSemaphore

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  4. 【转】3.4(译)构建Async同步基元,Part 4 AsyncBarrier

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  5. 【转】3.3(译)构建Async同步基元,Part 3 AsyncCountdownEvent

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  6. 【转】3.2(译)构建Async同步基元,Part 2 AsyncAutoResetEvent

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  7. 【转】1.4异步编程:轻量级线程同步基元对象

    开始<异步编程:同步基元对象(下)> 示例:异步编程:轻量级线程同步基元对象.rar 在<异步编程:线程同步基元对象>中我介绍了.NET4.0之前为我们提供的各种同步基元(包括 ...

  8. 【转】1.3异步编程:线程同步基元对象

    开始<异步编程:同步基元对象(上)> 示例:异步编程:线程同步基元对象.rar 如今的应用程序越来越复杂,我们常常需要多线程技术来提高我们应用程序的响应速度.每个线程都由自己的线程ID,当 ...

  9. 几何基元_.NET异步协调基元中的两种技术比较

    几何基元 Last week in my post on updating my Windows Phone 7 application to Windows 8 I shared some code ...

最新文章

  1. php 一行代码解决二维数组去重
  2. 如何用matlab实现文字动态滚动,js 动态文字滚动的例子
  3. uni 修改数据页面不重新渲染
  4. Windows下SVN已检出项目自动更新
  5. 实战Flash游戏开发
  6. java混淆书籍介绍,第二代Java混淆器Allatori功能介绍教程资源
  7. 需要在计算机安装msxml版本,Office2010安装需要MSXML版本6.10.1129.0的方法
  8. 张正友相机标定法原理与实现
  9. unity学习笔记-换装系统
  10. ZZULIOJ:1116: 删除元素
  11. 英语口语100之每日十句口语
  12. 网络安全进阶篇之免杀(十四章-9)MSF加密壳免杀过360
  13. 微信是怎么打造平台型生态商业模式的?
  14. 报表开发工具!DevExpress Reporting v19.1:WPF/Web平台报表
  15. 2018百度AI开发者大会以及个人对百度开发平台的观点
  16. 我们的空间是它圆——基于Poicare对宇宙的模型
  17. java递归算法[32]
  18. 火龙果(redpitaya)开发板常用接口C语言开发指南(九)——产生信号脉冲(持续更新中)
  19. mysql安全性_如何提高mysql的安全性
  20. web前端技术的学习(一)

热门文章

  1. JVM架构和GC垃圾回收机制详解
  2. 约瑟夫问题(Josephus Problem)的两种快速递归算法
  3. 汇编软件的安装与实验一
  4. 安装zabbix及LNMP的平台的搭建
  5. Spring的常见问题及答案
  6. STL中 map 和 multimap
  7. XmlDocument类
  8. windows下hadoop的单机伪分布式部署(3)
  9. C、CPP const 详解
  10. 用对拍程序来debug错误程序的错误数据