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

最近在学习.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 2 AsyncAutoResetEvent

上一篇,我们构建了async版本的ManualResetEvent。现在,让我们构建一个async版本的AutoResetEvent。

ManualResetEvent对象,当调用其Set()方法时,ManualResetEvent转化为有信号状态,并持有此信号直到显示调用其Reset()方法重置为无信号状态,调用Reset()重置为无信号状态导致下一次WaitOne()等待信号。相比之下,AutoResetEvent也在调用其Set()方法时获得信号,但是其在调用完WaitOne()后自动重置为无信号状态。比如,如果ManualResetEvent上有4个等待线程,当其中一个线程调用set()方法后,则4个等待线程都将完成。相比之下,如果AutoResetEvent上有4个等待线程,当其中一个线程调用set()方法后,仅仅是其中一个线程获得信号,其他3个WaitHandle依然为无信号状态(需要注意的是AutoResetEvent常常会导致无法跟踪信号的到达。比如:起初没有线程等待,事件接收了两次set()信号,然后两个线程再在事件上等待信号,这时只有其中一个完成等待---可以参考示例m_signaled变量的实现)。

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

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

首先,我们需要一些成员(eg:TaskCompletionSource<TResult>)。上一篇中的AsyncManualResetEvent,我们只需要单个TaskCompletionSource<TResult>实例,因为事件的通知会唤醒当前所有等待。但是AsyncAutoResetEvent不一样,我们需要区别对待不同的等待,因为如果有多个线程等待,一个信号到达只能唤醒一个等待。所以,我们需要一个TaskCompletionSource<TResult>实例集合。此外,信号到达时可能没有等待者,所以我们需要使用一个bool变量进行跟踪。最后,在一些情况下我们能重用已完成的Task来提高性能,所以我将一直保持其中一个引用,设计如下:

1
2
3
4
private readonly static Task s_completed = Task.FromResult(true);
private readonly Queue<TaskCompletionSource<bool> m_waits
                    = new Queue<TaskCompletionSource<bool>();
private bool m_signaled;

接下来,让我们实现一个命名为WaitAsync()的方法。当调用WaitAsync()时,如果m_signaled为true,我们直接返回已经完成的s_completed,因为这个等待调用消费这个信号,所以我们需要将m_signaled重置为false。如果m_signaled为false,我们将创建一个TaskCompletionSource<TResult>实例插入m_waits队列中,并且返回实例对应的Task。这个Task将推迟完成直到有线程调用Task的Set()方法并且唤醒等待者。但是要注意,这里需要保持多个操作执行的原子性,因此,我对m_waits队列加锁以确保同步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Task WaitAsync()
{
    lock (m_waits)
    {
        if (m_signaled)
        {
            m_signaled = false;
            returns_completed;
        }
        else
        {
            var tcs = new TaskCompletionSource<bool>();
            m_waits.Enqueue(tcs);
            return tcs.Task;
        }
    }
}

接下来,我将实现Set()方法。Set()方法将首先检查m_waits队列中是否有等待者。如果有,则从队列中取出一个TaskCompletionSource<bool>并且完成它。如果m_waits队列为空,则只是简单的将m_signaled设置为true。这里的操作需要保持原子性,并且要与WaitAsync()方法保持同步,所以set()的主体代码需要再一次对m_waits队列加锁。这里要注意一个重要的事情,在之前的文章中,我讨论了TaskCompletionSource<TResult>的 [Try]Set*() 系列方法,会使TaskCompletionSource<TResult>对应的Task作为同步调用的一部分运行。如果我们在lock内部调用SetResilt(),则Task的同步延续的运行将长时间持有lock。因此,我们释放lock后再调用Task的[Try]Set*() 系列方法来完成任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
public void Set()
{
    TaskCompletionSource<bool> toRelease = null
    lock (m_waits)
    {
        if (m_waits.Count> 0)
            toRelease = m_waits.Dequeue();
        else if (!m_signaled)
            m_signaled = true;
    }
    if (toRelease != null
        toRelease.SetResult(true);
}

这就是本节要讲的AsyncAutoResetEvent。

完整源码如下:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class AsyncAutoResetEvent
{
    // 保存一个成功完成的 Task<TResult>,供重用以提高性能
    private readonly static Task s_completed = Task.FromResult(true);
    // 等待任务队列
    private readonly Queue<TaskCompletionSource<bool>> m_waits = new Queue<TaskCompletionSource<bool>>();
    // 用于跟踪 信号到达时可能没有等待者 的情况,将AsyncAutoResetEvent的初始状态设置为有信号
    private bool m_signaled;
 
    public Task WaitAsync()
    {
        lock (m_waits)
        {
            if (m_signaled)
            {
                m_signaled = false;
                return s_completed;
            }
            else
            {
                var tcs = new TaskCompletionSource<bool>();
                m_waits.Enqueue(tcs);
                return tcs.Task;
            }
        }
    }
 
    public void Set()
    {
        TaskCompletionSource<bool> toRelease = null;
        lock (m_waits)
        {
            if (m_waits.Count > 0)
                toRelease = m_waits.Dequeue();
            else if (!m_signaled)
                m_signaled = true;
        }
        if (toRelease != null)
            toRelease.SetResult(true);
    }
}

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

推荐阅读:

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

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

感谢你的观看……

原文:《Building Async Coordination Primitives, Part 2: AsyncAutoResetEvent》

作者:Stephen Toub – MSFT

【转】3.2(译)构建Async同步基元,Part 2 AsyncAutoResetEvent相关推荐

  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.1(译)构建Async同步基元,Part 1 AsyncManualResetEvent

    传送门:异步编程系列目录-- 最近在学习.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. 期末Linux复习容易迷糊的地方!
  2. 代码实战 | 用LeGO-LOAM实现BFS点云聚类和噪点剔除
  3. ajax 跨域请求,每次会发送两个请求?
  4. 微信小程序开发第四弹
  5. yii2手册_史上最全的技术手册整理总结,编程小白都从这篇文章迅速成为大牛...
  6. C++中常量成员函数的含义
  7. 砂 即懒且忙 只有随笔
  8. 连接查询_左连接/右连接/全连接的区别
  9. 作者:袁明轩(1980-),男,华为诺亚方舟实验室研究员。
  10. 分析go程序内存逃逸情况
  11. r语言数据变量分段_使用R语言实现数据分段
  12. 【Elasticsearch】Elasticsearch mapping 映射对象
  13. 【Java】URL下载网络资源(CloudMusic)
  14. 【Web前端】怎样用记事本写一个简单的网页-html
  15. 时间序列学习(5):ARMA模型定阶(AIC、BIC准则、Ljung-Box检验)
  16. UG二次开发-程序视图根组
  17. 条条道路通罗马感受古罗马帝国的辉煌时代!
  18. 随机生成20以内加减法,5次答题并统计正确和错误题数
  19. 瑞吉外卖项目——瑞吉外卖
  20. 百度云搜索引擎森林战士_002 找到你要的资料——百度网盘使用指南

热门文章

  1. ACM学习历程—HDU2068 RPG的错排(组合数学)
  2. iReport报表工具的使用
  3. DELPHI串口通讯编程
  4. 住宅的结构有哪些形式
  5. [密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第12篇]椭圆曲线上的群理论是什么
  6. [JAVA][算法] [字符串匹配]KMP
  7. python聚类分析结果很差_python中的聚类分析:value错误:x和y的大小必须相同
  8. 数据流图怎么画_概率图模型怎么画?5个步骤完成专业模型图
  9. 电脑功耗监测_应急监测便携式VOC检测仪色谱分析仪原理解析
  10. windows server 触屏_宜昌触屏万能蒸烤箱价格-华春新能源有限公司