如何取消 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
操作,所以我期望信号量 CurrentCount
为 1
而 Task
位于 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
不会 保证等待,阻塞当前线程,直到所有监听该取消标记的操作都被取消(及其父操作等)。
换句话说,您的代码具有固有的竞争条件:Release
或 CancellationToken
的取消是否会先到达 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
作为一个。
考虑以下代码,使用 .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
操作,所以我期望信号量 CurrentCount
为 1
而 Task
位于 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
不会 保证等待,阻塞当前线程,直到所有监听该取消标记的操作都被取消(及其父操作等)。
换句话说,您的代码具有固有的竞争条件:Release
或 CancellationToken
的取消是否会先到达 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
作为一个。