SemaphoreSlim 等待优先级

问题描述 投票:0回答:2

我想知道

SemaphoreSlim
在调用 Await 时是否有类似优先级的东西。

我找不到任何东西,但也许有人以前做过类似的事情。

这个想法是,如果我需要,稍后可以以更高的优先级在信号量上调用等待,并且它将允许等待首先返回。

c# .net asynchronous async-await semaphore
2个回答
5
投票

这里有一个可以优先获得的职业

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
)。


4
投票

不,

SemaphoreSlim
中没有优先级,无论您使用同步锁定还是异步锁定。

异步锁很少需要优先级。如果您退后一步并着眼于更大的前景,通常这类问题会有更优雅的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.