.NET的可调信号量

目录

介绍

背景

使用代码

兴趣点


  • 下载源代码19.5 KB

介绍

当控制活动任务/线程的数量时,SemaphoreSemaphoreSlim是有用且有价值的组件。但是,现有的SemaphoreSemaphoreSlim实现不允许实例化后重新调整信号量的大小——因此,无法通过调整实例化信号量的大小来限制任务/线程。

背景

使用信号量作为限制或限制应用程序的方法是一种众所周知的方法,但是——在.NET世界中,无法通过某种方式调整实例化的信号量或SemaphoreSlim的大小,从而允许对要进行的节流进行调整。

Java中,AdjustableSemaphore中已经存在了一段时间——在着眼于实现.NET时,我遇到了一系列讨论这一确切要求的帖子:

  • 如何实现动态信号量?

调整一些小的功能更改,以允许初始化大小为0zero)的AdjustableSemaphore,进行一些命名和声明更新,以及满足我的需求的全功能的AdjustableSemaphore

使用代码

AdjustableSemaphore本身是一个很简单的类:

using System;
using System.Threading;

namespace CPSemaphore
{
    /// 
    /// Implementation of an AdjustableSemaphore - similar to that provided 
    /// in the Java namesake, allowing for the Semaphore to be resizeddynamically 
    /// https://github.com/addthis/basis/blob/master/basis-core/src/main/java/com/
    /// addthis/basis/util/AdjustableSemaphore.java 
    /// .NET version originally published at the following URL 
    /// https://social.msdn.microsoft.com/Forums/vstudio/en-US/
    /// 5b648588-298b-452e-bc9a-1df0258242fe/
    /// how-to-implement-a-dynamic-semaphore?forum=netfxbcl 
    /// Minor alterations made, allowing for initialization using 0 as Semaphore size, 
    /// syntax and naming convention changes 
    /// 

    public class AdjustableSemaphore
    {
        private static readonly object m_LockObject = new object();
        private int m_AvailableCount = 0;
        private int m_MaximumCount = 0;

        public AdjustableSemaphore(int maximumCount)
        {
            MaximumCount = maximumCount;
        }

        public int MaximumCount
        {
            get
            {
                lock (m_LockObject)
                {
                    return m_MaximumCount;
                }
            }
            set
            {
                lock (m_LockObject)
                {
                    if (value < 0)
                    {
                        throw new ArgumentException("Must be greater than or equal to 0.", 
                                                    "MaximumCount");
                    }
                    m_AvailableCount += value - m_MaximumCount; // m_AvailableCount 
                                        // can be < 0, resize will not affect active semaphores
                    m_MaximumCount = value;
                    Monitor.PulseAll(m_LockObject);
                }
            }
        }

        public void WaitOne()
        {
            lock (m_LockObject)
            {
                while (m_AvailableCount <= 0)
                {
                    Monitor.Wait(m_LockObject);
                }
                m_AvailableCount--;
            }
        }

        public void Release()
        {
            lock (m_LockObject)
            {
                if (m_AvailableCount < m_MaximumCount)
                {
                    m_AvailableCount++;
                    Monitor.Pulse(m_LockObject);
                }
                else
                {
                    throw new SemaphoreFullException("Adding the given count 
                    to the semaphore would cause it to exceed its maximum count.");
                }
            }
        }

        public void GetSemaphoreInfo
               (out int maxCount, out int usedCount, out int availableCount)
        {
            lock (m_LockObject)
            {
                maxCount = m_MaximumCount;
                usedCount = m_MaximumCount - m_AvailableCount;
                availableCount = m_AvailableCount;
            }
        }
    }
}

在本文的示例代码中,我整理了一个小示例控制台应用程序,该应用程序显示了如何动态更改adjutableSemaphore大小。

在此示例中,AdjustableSemaphore将使用大小为0(零)(AdjustableSemaphore semaphoreObject = new AdjustableSemaphore(0);)初始化 

总共启动了十个任务,等待Semaphore

AdjustableSemaphore的大小调整为6,(semaphoreObject.MaximumCount = 6;),这时将开始执行前6个任务,然后开始处理。

一秒钟的暂停后,AdjustableSemaphore调整为3。所有正在运行的任务都不会受到影响,但是直到有可用的信号量之前,没有新任务会启动,直到原始6的第四个任务完成并发布后,才会发生。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace CPSemaphore
{
    class Program
    {
        static void Main(string[] args)
        { 
            AdjustableSemaphore semaphoreObject = new AdjustableSemaphore(0);
            List tasks = new List();

            for (int i = 0; i< 10; ++i)
            {
                int j = i;
                tasks.Add(Task.Factory.StartNew(() =>
                    {
                        Random rnd = new Random();
                        semaphoreObject.WaitOne();
                        semaphoreObject.GetSemaphoreInfo(out int maxCount, 
                        out int usedCount, out int availableCount);
                        Console.WriteLine("{0} - Acquired Semaphore - Semaphore Max: {1}, 
                        Used: {2}, Available: {3}", j, maxCount, usedCount, availableCount);
                        Thread.Sleep(rnd.Next(1000, 5000));
                        semaphoreObject.Release();
                        semaphoreObject.GetSemaphoreInfo(out maxCount, out usedCount, 
                                                         out availableCount);
                        Console.WriteLine("{0} - Released Semaphore - Semaphore Max: {1}, 
                        Used: {2}, Available: {3}", j, maxCount, usedCount, availableCount);
                    }));
            }

            semaphoreObject.MaximumCount = 6;
            Console.WriteLine("Awaiting All Tasks");
            Thread.Sleep(1000);
            semaphoreObject.MaximumCount = 3;
            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("All Tasks Complete");
            Console.ReadLine();
        }
    }
}

运行的输出将类似于以下内容:

Awaiting All Tasks
6 - Aquired Semaphore - Semaphore Max: 6, Used: 6, Available: 0
5 - Aquired Semaphore - Semaphore Max: 6, Used: 6, Available: 0
1 - Aquired Semaphore - Semaphore Max: 6, Used: 6, Available: 0
4 - Aquired Semaphore - Semaphore Max: 6, Used: 6, Available: 0
3 - Aquired Semaphore - Semaphore Max: 6, Used: 6, Available: 0
7 - Aquired Semaphore - Semaphore Max: 6, Used: 6, Available: 0
3 - Released Semaphore - Semaphore Max: 3, Used: 5, Available: -2
1 - Released Semaphore - Semaphore Max: 3, Used: 4, Available: -1
7 - Released Semaphore - Semaphore Max: 3, Used: 3, Available: 0
4 - Released Semaphore - Semaphore Max: 3, Used: 2, Available: 1
9 - Aquired Semaphore - Semaphore Max: 3, Used: 3, Available: 0
5 - Released Semaphore - Semaphore Max: 3, Used: 1, Available: 2
6 - Released Semaphore - Semaphore Max: 3, Used: 2, Available: 1
8 - Aquired Semaphore - Semaphore Max: 3, Used: 2, Available: 1
2 - Aquired Semaphore - Semaphore Max: 3, Used: 3, Available: 0
9 - Released Semaphore - Semaphore Max: 3, Used: 2, Available: 1
0 - Aquired Semaphore - Semaphore Max: 3, Used: 3, Available: 0
2 - Released Semaphore - Semaphore Max: 3, Used: 2, Available: 1
8 - Released Semaphore - Semaphore Max: 3, Used: 1, Available: 2
0 - Released Semaphore - Semaphore Max: 3, Used: 0, Available: 3
All Tasks Complete

从样本输出中可以看出,将AdjustableSemaphore6调整为3会导致负数的信号量可用,直到完成并释放第三个任务(数字7)为止,此时可用计数为0

任务四(数字4)完成后,可用计数为1,然后运行新任务(数字9)。

兴趣点

AdjustableSemaphore将允许你通过动态地调整AdjustableSemaphore来调节处理,并且它在过去当我不得不实现动态的缩放过程时很方便。

你可能感兴趣的:(CSharp.NET)