目录
介绍
背景
使用代码
兴趣点
当控制活动任务/线程的数量时,Semaphore和SemaphoreSlim是有用且有价值的组件。但是,现有的Semaphore和SemaphoreSlim实现不允许实例化后重新调整信号量的大小——因此,无法通过调整实例化信号量的大小来限制任务/线程。
使用信号量作为限制或限制应用程序的方法是一种众所周知的方法,但是——在.NET世界中,无法通过某种方式调整实例化的信号量或SemaphoreSlim的大小,从而允许对要进行的节流进行调整。
在Java中,AdjustableSemaphore中已经存在了一段时间——在着眼于实现.NET时,我遇到了一系列讨论这一确切要求的帖子:
调整一些小的功能更改,以允许初始化大小为0(zero)的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
从样本输出中可以看出,将AdjustableSemaphore从6调整为3会导致负数的信号量可用,直到完成并释放第三个任务(数字7)为止,此时可用计数为0。
任务四(数字4)完成后,可用计数为1,然后运行新任务(数字9)。
AdjustableSemaphore将允许你通过动态地调整AdjustableSemaphore来调节处理,并且它在过去当我不得不实现动态的缩放过程时很方便。