前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2019-12-1-使用SemaphoreSlim实现异步等待

2019-12-1-使用SemaphoreSlim实现异步等待

作者头像
黄腾霄
发布2020-06-10 15:06:29
1.6K0
发布2020-06-10 15:06:29
举报
文章被收录于专栏:黄腾霄的博客黄腾霄的博客

Semaphore大家应该都很熟悉,一个能够指定最大并发个数的同步锁。

实际上.NET还有一个轻量级的信号量实现SemaphoreSlim,其不但能够更加高效的实现信号量的功能,还提供了一个异步等待的API。


我们首先回忆下SemaphoreSlim同步等待时的情况。

代码语言:javascript
复制
    class Program
    {
        private static SemaphoreSlim semaphore;
        static void Main(string[] args)
        {
            semaphore = new SemaphoreSlim(1, 1);
            var tasks = new Task[2];

            for (int i = 0; i <= 1; i++)
            {
                tasks[i] = Task.Run(() =>
                {
                    Console.WriteLine("Task {0}等待信号量",
                        Task.CurrentId);
                    semaphore.Wait();
                    Console.WriteLine("Task {0} 获得信号量.", Task.CurrentId);
                    //模拟做事情况
                    Thread.Sleep(1000);

                    Console.WriteLine("Task {0} 释放信号量; 释放前数目: {1}.",
                        Task.CurrentId, semaphore.Release());
                });
            }

            // 等待task执行完成.
            Task.WaitAll(tasks);
            Console.ReadLine();
        }
    }

如上所示的代码中,SemaphoreSlim的初始信号数是1,且同时只能有一个线程获得锁。

此时我们的代码会依次执行。

image-20191201171846218
image-20191201171846218

但是此时task1和task2 的线程是阻塞的。那么在UI线程上使用,尤其是WPF这种单线程应用来说,几乎是不可行的。

此时我们就可以使用今天的主角WaitAsync

我们稍稍修改下代码

代码语言:javascript
复制
    class Program
    {
        private static SemaphoreSlim semaphore;

        static void Main(string[] args)
        {
            semaphore = new SemaphoreSlim(, );
            var tasks = new Task[];

            for (int i = ; i <= ; i++)
            {
                tasks[i] = Task.Run(async () =>
                {
                    var currentId = Task.CurrentId;
                    Console.WriteLine("Task {0}等待信号量",
                        currentId);
                    Foo(currentId);
                    Console.WriteLine("Task {0} 执行非同步区代码.", currentId);
                    Thread.Sleep();
                    Console.WriteLine("Task {0} 非同步区代码执行完成.", currentId);
                });
            }

            // 等待task执行完成.
            Task.WaitAll(tasks);
            Console.ReadLine();
        }

        public static async Task Foo(int? id)
        {
            var currentId = id;
            await semaphore.WaitAsync();
            Console.WriteLine("Task {0} 获得信号量.", currentId);
            //模拟做事情况
            Console.WriteLine("Task {0} 执行同步区代码.", currentId);
            Thread.Sleep();
            Console.WriteLine("Task {0} 同步区代码执行完成.", currentId);
            Console.WriteLine("Task {0} 释放信号量; 释放前数目: {1}.",
                currentId, semaphore.Release());
        }
    }

我们将同步区执行的代码抽取到方法Foo中,而内部调用WaitAsync

结果如下所示

image-20191201173336292
image-20191201173336292

我们看到task3在没有获得同步锁的情况下WaitAsync直接返回线程控制权,所以task3线程没有被阻塞能够优先执行非同步区代码。而在获得信号量之后,继续执行同步区代码。


参考文档:


本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/%E4%BD%BF%E7%94%A8SemaphoreSlim%E5%AE%9E%E7%8E%B0%E5%BC%82%E6%AD%A5%E7%AD%89%E5%BE%85.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-12-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档