我想知道 SemaphoreSlim
在调用 Await 时是否有类似优先级的功能。
我没有找到任何相关信息,但也许有人之前做过这样的事情。
想法是,如果需要的话,稍后可以使用更高的优先级在 Semaphore 上调用 await,并允许 await 先返回。
我想知道 SemaphoreSlim
在调用 Await 时是否有类似优先级的功能。
我没有找到任何相关信息,但也许有人之前做过这样的事情。
想法是,如果需要的话,稍后可以使用更高的优先级在 Semaphore 上调用 await,并允许 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 = null)
{
if (initialCount < 0)
throw new ArgumentOutOfRangeException(nameof(initialCount));
if (maxCount <= 0)
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 = null)
: this(initialCount, Int32.MaxValue, comparer) { }
public PrioritySemaphore(IComparer<TPriority> comparer = null)
: 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.TrySetResult();
}
}
使用示例:
PrioritySemaphore<int> semaphore = new();
//...
await semaphore.WaitAsync(priority: 1);
//...
await semaphore.WaitAsync(priority: 2);
//...
semaphore.Release();
Release
后,信号量将被具有最高优先级的等待者获取。在上面的示例中,它将是具有优先级 1
的等待者。较小的值表示更高的优先级。如果有多个等待者具有相同的最高优先级,则将通过首先请求它的那个等待者来获取信号量。保持 FIFO 顺序是将 TPriority
与 long
耦合在上述实现中的原因。
PrioritySemaphore<TPriority>
类仅具有异步 API,并且不支持带有取消或超时的等待。要获取具有更多功能并且还可以编译 .NET 版本早于 6 的版本,请参见此答案的 第五版(基于更灵活但效率较低的 SortedSet
)。
不,无论您是使用同步还是异步锁定,在SemaphoreSlim
中都没有优先级。
使用异步锁很少需要优先级。通常,如果您退后一步并查看更大的图片,这些问题通常有更优雅的解决方案。