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

最近在学习.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 5 AsyncSemaphore

在之前的文章中,我已经构建了AsyncManualResetEvent、AsyncAutoResetEvent、AsyncCountdownEvent和AsyncBarrier。在这篇文章中,我将构建一个AsyncSemaphore类。

Semaphore(信号量)被广泛使用。一个重要应用就是限流,为了保护访问限制性资源。在.NET中我们有两个信号量类型即:Semaphore(封装Win32函数)和SemaphoreSlim(封装Monitor实现的轻量版)。现在我将构建一个简单的Async版本,待构建的目标类型如下:

1
2
3
4
5
6
public class AsyncSemaphore
{
    public AsyncSemaphore(int initialCount);
    public Task WaitAsync();
    public void Release();
}

我们需要一些成员变量,这些成员变量几乎和AsyncAutoResetEvent成员一样,变量存在的作用也类似。我们需要能够唤醒单个等待者,所以我们维护一个TaskCompletionSource<bool>的实例队列。我们需要跟踪信号量的当前计数,以便我们知道在开始阻塞之前还有多少等待者可以完成。并且我们会发现,有一个高效的措施,即我们维护一个已经完成的Task以供重用。

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

类的构造函数仅仅是初始化请求计数:

1
2
3
4
5
6
public AsyncSemaphore(int initialCount)
{
    if (initialCount< 0)
        throw new ArgumentOutOfRangeException("initialCount");
    m_currentCount = initialCount;
}

现在让我们来构建WaitAsync()方法。此方法中我们要使用lock确保所有操作以原子方式执行,并且要确保与Release()方法保持同步。然后,这里有两种情况。如果当前m_currentCount大于0,即还没有到达semaphore限流界限,所以这个等待操作能立即完成。在这种情况下,我们递减当前计数并且返回缓存的已经完成的任务s_completed(即在无争用的情况下我们不需要创建新的TaskCompletionSource<bool>实例)。如果当前计数m_currentCount为0,我们创建一个新的TaskCompletionSource<bool>实例添加到队列中,并且返回实例对应的Task给调用者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Task WaitAsync()
{
    lock (m_waiters)
    {
        if (m_currentCount > 0)
        {
            --m_currentCount;
            return s_completed;
        }
        else
        {
            var waiter = new TaskCompletionSource<bool>();
            m_waiters.Enqueue(waiter);
            return waiter.Task;
        }
    }
}

在Release()方法中,如果队列中还有等待者,则从队列中删除一个等待者并且完成对应的TaskCompletionSource<bool>实例。如果没有等待者,则简单的递增当前计数m_currentCount。这里的操作需要保持原子性,并且要与WaitAsync()方法保持同步,所以Release()的主体代码需要再一次对m_waiters队列加锁。这里要注意一个重要的事情,在之前的文章中,我讨论了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 Release()
{
    TaskCompletionSource<bool> toRelease = null;
    lock (m_waiters)
    {
        if (m_waiters.Count > 0)
            toRelease = m_waiters.Dequeue();
        else
            ++m_currentCount;
    }
    if (toRelease != null)
        toRelease.SetResult(true);
}

这就是本节要讲的AsyncSemaphore。

完整源码如下:

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
42
43
44
45
46
47
48
public class AsyncSemaphore
{
    // 维护一个已经完成的Task以供重用提高效率
    private readonly static Task s_completed = Task.FromResult(true);
    private readonly Queue<TaskCompletionSource<bool>> m_waiters
                                = new Queue<TaskCompletionSource<bool>>();
    // 跟踪信号量的当前计数,以便我们知道在开始阻塞之前还有多少等待者可以完成
    private int m_currentCount;
 
    public AsyncSemaphore(int initialCount)
    {
        if (initialCount < 0)
            throw new ArgumentOutOfRangeException("initialCount");
        m_currentCount = initialCount;
    }
 
    public Task WaitAsync()
    {
        lock (m_waiters)
        {
            if (m_currentCount > 0)
            {
                --m_currentCount;
                return s_completed;
            }
            else
            {
                var waiter = new TaskCompletionSource<bool>();
                m_waiters.Enqueue(waiter);
                return waiter.Task;
            }
        }
    }
 
    public void Release()
    {
        TaskCompletionSource<bool> toRelease = null;
        lock (m_waiters)
        {
            if (m_waiters.Count > 0)
                toRelease = m_waiters.Dequeue();
            else
                ++m_currentCount;
        }
        if (toRelease != null)
            toRelease.SetResult(true);
    }
}

下一节,我们将看看如何使用一个AsyncSemaphore来实现一个作用域互斥锁定机制。

推荐阅读:

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

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

感谢你的观看……

原文:《Building Async Coordination Primitives, Part 5: AsyncSemaphore》

【转】3.5(译)构建Async同步基元,Part 5 AsyncSemaphore相关推荐

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

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

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

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

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

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

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

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

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

    传送门:异步编程系列目录-- 最近在学习.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. vim学习笔记(一)
  2. R语言data.table导入数据实战:data.table中编写函数并使用SD数据对象
  3. FPGA管脚分配文件的保存
  4. poj3279 Fliptile
  5. 群晖linux怎么进入u盘,黑群辉直接更改启动u盘的sn、mac等信息
  6. ASP.NET WebAPI 自定义ControllerSelector
  7. 云计算(cloud computing)十大问答
  8. 小猿圈python学习-函数的递归
  9. maltab求解微分方程
  10. UI设计实用素材|闪屏模板
  11. 【VB6 Gdi+进阶】序章
  12. 北大计算机学硕几年,我的OI经历 --张正超(深学教育资深教练、北大计算机系研究生)...
  13. linux下xp镜像文件iso下载,Windows XP SP3简体中文专业版[官方MSDN正式原版ISO镜像]
  14. 生命游戏(康威的生命游戏) Game Of Life - Conway Cocos Creator 3.x版实现
  15. 通俗易懂的AI算法原理
  16. 机器学习实战(一)KNN算法
  17. 微信小程序背景图真机显示不了
  18. Linux运维常用知识(1)
  19. iOS编译cURL库并链接darwinssl,zlib,c-ares库操作步骤
  20. python制作浏览器 脚本之家_基于Python打造账号共享浏览器功能

热门文章

  1. 小程序·云开发实战 - 体重记录小程序
  2. java处理url中的特殊字符%等
  3. IE8下强制浏览器用哪个IE版本渲染页面
  4. 实验吧—Web——WP之 Guess Next Session
  5. luogu P1659 养猪 dp 好理解
  6. 【原创】Kakfa utils源代码分析(一)
  7. DataGridView数据导入到Excel 中
  8. jQuery 实现一个简单的信息反馈或者信息收集的页面
  9. 算法五——字符串匹配(中)
  10. Breadth-first Search(广度优先搜索)专题2