我想知道
SemaphoreSlim
在调用 Await 时是否有类似优先级的东西。
我找不到任何东西,但也许有人以前做过类似的事情。
这个想法是,如果我需要,稍后可以以更高的优先级在信号量上调用等待,并且它将允许等待首先返回。
这里有一个可以优先获得的职业
PrioritySemaphore<TPriority>
。在内部,它基于 PriorityQueue<TElement, TPriority>
集合 (.NET 6)。
public class PrioritySemaphore<TPriority>
{
private readonly PriorityQueue<TaskCompletionSource, (TPriority, long)> _queue;
private readonly int _maxCount;
private int _currentCount;
private long _indexSeed = 0;
public PrioritySemaphore(int initialCount, int maxCount,
IComparer<TPriority> comparer = default)
{
if (initialCount < 0)
throw new ArgumentOutOfRangeException(nameof(initialCount));
if (maxCount <= 0 || maxCount < initialCount)
throw new ArgumentOutOfRangeException(nameof(maxCount));
comparer ??= Comparer<TPriority>.Default;
_queue = new(Comparer<(TPriority, long)>.Create((x, y) =>
{
int result = comparer.Compare(x.Item1, y.Item1);
if (result == 0) result = x.Item2.CompareTo(y.Item2);
return result;
}));
_currentCount = initialCount;
_maxCount = maxCount;
}
public PrioritySemaphore(int initialCount, IComparer<TPriority> comparer = default)
: this(initialCount, Int32.MaxValue, comparer) { }
public PrioritySemaphore(IComparer<TPriority> comparer = default)
: this(0, Int32.MaxValue, comparer) { }
public int CurrentCount => Volatile.Read(ref _currentCount);
public Task WaitAsync(TPriority priority)
{
lock (_queue)
{
Debug.Assert((_queue.Count == 0) || (_currentCount == 0));
if (_currentCount > 0)
{
_currentCount--;
return Task.CompletedTask;
}
TaskCompletionSource tcs = new(
TaskCreationOptions.RunContinuationsAsynchronously);
_queue.Enqueue(tcs, (priority, ++_indexSeed));
return tcs.Task;
}
}
public void Release()
{
TaskCompletionSource tcs;
lock (_queue)
{
Debug.Assert((_queue.Count == 0) || (_currentCount == 0));
if (_queue.Count == 0)
{
if (_currentCount >= _maxCount) throw new SemaphoreFullException();
_currentCount++;
return;
}
tcs = _queue.Dequeue();
}
tcs.SetResult();
}
}
使用示例:
PrioritySemaphore<int> semaphore = new(initialCount: 0);
//...
await semaphore.WaitAsync(priority: 1);
//...
await semaphore.WaitAsync(priority: 2);
//...
semaphore.Release();
在
Release
之后,信号量将被优先级最高的awaiter获取。在上面的示例中,它将是具有优先级 1
的等待者。值越小表示优先级越高。如果有多个等待者具有相同的最高优先级,则信号量将由第一个请求的等待者获取。维持 FIFO 顺序是在上述实现中将 TPriority
与 long
耦合的原因。
类
PrioritySemaphore<TPriority>
只有异步API,不支持取消或超时等待。对于具有更多功能并且还可以在早于 6 的 .NET 版本上进行编译的版本,请参阅此答案的第 5 版(基于更灵活但效率较低的 SortedSet
)。
不,
SemaphoreSlim
中没有优先级,无论您使用同步锁定还是异步锁定。
异步锁很少需要优先级。如果您退后一步并着眼于更大的前景,通常这类问题会有更优雅的解决方案。