如何取消 SemaphoreSlim WaitAsync 方法

How to cancel SemaphoreSlim WaitAsync method

考虑以下代码,使用 .NET 5 执行:

using System;
using System.Threading;

public class Program
{
    public static void Main(string[] args)
    {
        var semaphore = new SemaphoreSlim(0);

        var cts = new CancellationTokenSource();

        var entrance = semaphore.WaitAsync(cts.Token);

        cts.Cancel();
        cts.Dispose();

        semaphore.Release();

        Console.WriteLine("Entrance status: " + entrance.Status);
        Console.WriteLine("Current count: " + semaphore.CurrentCount);
    }
}

当我 运行 这段代码时,应用程序成功完成,我得到以下结果:

Entrance status: WaitingForActivation

Current count: 0

但是由于我在释放信号量之前取消了 WaitAsync 操作,所以我期望信号量 CurrentCount1Task 位于 Canceled状态。

在发布问题之前,我 运行 https://dotnetfiddle.net 中的代码,令人惊讶的是,它 运行 正如我预期的 .NET Framework 4.7.2.

我是否在 .NET 5 中发现了错误SemaphoreSlim

有没有办法在 .NET 5 中获得以前的行为?

PS:我找到了一种通过为 WaitAsync 操作设置超时来获得前一种行为的方法,但我认为这不是一个可接受的答案。

根据 Guru Stron 评论进行编辑

Release 语句生成 OperationCancelledException 之前等待 entrance,如我所料。

但是在语句之​​后等待不会抛出任何异常并且信号量被“消耗”。

这两种情况都会在前 .NET Framework 中产生错误。

CancellationTokenSource.Cancel是请求取消。取消标记会立即被取消,但 Cancel 不会 保证等待,阻塞当前线程,直到所有监听该取消标记的操作都被取消(及其父操作等)。

换句话说,您的代码具有固有的竞争条件:ReleaseCancellationToken 的取消是否会先到达 WaitAsync 操作。如果 Release 先到达,那么信号量将被获取。如果取消先到达,则取消等待。

await 在调用 Release 之前执行 WaitAsync 操作的任务通过 (a) 等待操作解决此竞争条件,强制它在 [=12= 之前看到取消] 已发送。

Do I find a bug in .NET 5 SemaphoreSlim?

没有。该代码取决于 .NET Framework 和 .NET 5 上的竞争条件。不保证竞争条件的结果在任一平台上都有任何特定结果。

Is there a way to get the former behavior in .NET 5?

没有。我建议重新编写代码,使其不依赖于竞争条件。然后它将在两个平台上正常工作。

代码 可能 SemaphoreSlim 用于非设计用途。例如,如果您需要一个异步工作队列,那么构建一个异步工作队列而不是尝试使用 SemaphoreSlim 作为一个。