C# 线程同步基元-CountdownEvent

CountdownEvent-在收到特定次数信号后使等待线程继续运行的同步基元

1 CountdownEvent内部使用ManualResetEventSlim来实现信号量同步的

在CountdownEvent源码里有一个ManualResetEventSlim变量,源码如下:

 

2 CountdownEvent实例CurrentCount属性

CurrentCount属性:被阻止线程继续运行还需要收到的剩余信号量数

看一下源码就知道了:

 其中的m_currentCount的值正常情况下就是CurrentCount属性Get的返回值

3 CountdownEvent实例Wait方法

Wait方法阻止当前线程,直到收到指定注册的信号量数后才会继续运行Wait后面的代码,Wait方法有一个以毫秒为单位超时参数,如果等待时间超过了这个时间,就继续运行Wait后面的代码,防止无限等待;如果Wait方法没有指定超时参数默认是无限等待(即超时参数设置为-1)

 Wait方法源码如下:

 可以看到,Wait阻止线程继续运行是通过ManualResetEventSlim的Wait方法来实现的,Wait方法是否阻塞其所在的线程是根据IsSet值来判断的,IsSet值获取方法源码如下:

4 CountdownEvent实例Signal()方法

CountdownEvent是通过ManualResetEventSlim的Set方法来通知被阻止的线程继续运行的

CountdownEvent的Signal方法用来向CountdownEvent实例注册信号并减少CurrentCount属性值,即注册了n个信号,则CurrentCount就会减少n,当CurrentCount<=0时被阻止线程就会继续运行

CountdownEvent的Signal()方法作用:向CountdownEvent注册1次信号,并将CountdownEvent.CurrentCount值原子减1

Signal(n):将CountdownEvent实例注册n个信号并将CurrentCount属性值减n

 

5 CountdownEvent使用示例

一般的使用步骤:在主调线程里定义一个多线程共用的同一个CountdownEvent实例,然后在多线程的运行方法里参数里接收这个CountdownEvent实例

多线程TaskMethod方法如下:

        public static async Task<string> CountdownEventTaskMethod(string taskName, double seconds, CountdownEvent countdownEvent, CancellationToken cancellationToken)
        {
            await Task.Delay(TimeSpan.FromSeconds(seconds));
            Console.WriteLine($"注册信号前 [{taskName}] 继续运行还需要注册 [{ countdownEvent.CurrentCount }] 个信号量等待线程才会继续运行");

            Console.WriteLine($"{taskName} 注册1次信号[Signal()]");
            countdownEvent.Signal(); // 向CountdownEvent注册信号,并减少CountdownEvent.CurrentCount值

            Console.WriteLine($"注册信号后 [{taskName}] 还需要注册 [{ countdownEvent.CurrentCount }] 个信号量等待线程才会继续运行");

            return $"{taskName} ok";
        }

测试方法代码如下:

        public static void CountdownEventTest()
        {
            // CountdownEvent:是在收到信号特定次数后取消阻止等待线程的同步基元
            // CountdownEvent:在计数变为0(即CurrentCount=0)时处于有信号状态的同步基元
            CountdownEvent countdownEvent = new CountdownEvent(2);// 设置信号特定次数初始值为2
            using (countdownEvent) 
            {
                var task1 = CountdownEventTaskMethod("task1", 1, countdownEvent, default);
                var task2 = CountdownEventTaskMethod("task2", 2, countdownEvent, default);
                var task3 = CountdownEventTaskMethod("task3", 3, countdownEvent, default);
                var task4 = CountdownEventTaskMethod("task3", 4, countdownEvent, default);

                Console.WriteLine("开始等待收到特定次数信号");
                // 阻止当前线程,直到收到信号特定次数(这里是2次)继续运行
                // 也可以理解为countdownEvent.CurrentCount=0时继续运行
                countdownEvent.Wait();
                Console.WriteLine($"已收到了信号特定次数继续运行,countdownEvent 当前还需要注册的信号量数量[{countdownEvent.CurrentCount}]");
            }
        }

启动方法:

        static void Main(string[] args)
        {
            CountdownEventTest();

            Console.Read();
        }

运行结果:

 

微软官方文档

https://docs.microsoft.com/zh-cn/dotnet/standard/threading/countdownevent

posted @ 2021-01-01 14:22  温故纳新  阅读(280)  评论(0编辑  收藏  举报