我正在编写一个.NET标准2.0库,该库将具有相同功能的同步和异步版本,其中一个功能需要支持取消的延迟。
我试图想出一种方法来等待一个特定的时间,在所有的场景下都不会出现死锁或其他障碍。一个担心是 在异步方法上同步等待会导致死锁。.
考虑到这个类,你应该如何实现 Wait
以使其在任何地方都安全?呼叫 WaitAsync
不是需求,等待完全可以单独实现。
class Waiter
{
// Easy enough
public async Task WaitAsync(TimeSpan delay, CancellationToken token = default)
{
try
{
await Task.Delay(delay, token)
}
catch(OperationCancelledException)
{
}
}
// Not so straightforward
public void Wait(TimeSpan delay, CancellationToken token = default)
{
WaitAsync(delay, token).GetAwaiter().GetResult(); // May deadlock
WaitAsync(delay, token).Wait(); // May deadlock, also doesn't propagate exceptions properly
WaitAsync(delay).Wait(token); // Even worse
Thread.Sleep(delay) // Doesn't support cancellation
token.WaitHandle.WaitOne(delay, true); // Maybe? Not sure how the second parameter works when I have no control over the context
token.WaitHandle.WaitOne(delay, false); // No idea
}
}
这个答案 几乎 回答了你所有的问题。具体来说,它解释了通过让其中一个调用另一个来实现同步和异步API不是一个好主意。如果你遇到了这种情况,我推荐你使用 布尔论点黑客.
对于你的具体问题,理想情况下,你会使用一个同步等待(Thread.Sleep
),但由于它不支持 CancellationToken
这不是一个选项。所以你需要自己写。正如你问题中提到的。WaitHandle
有一个你可以使用的超时参数。
public void Wait(TimeSpan delay, CancellationToken token = default)
{
token.WaitHandle.WaitOne(delay);
}
值得一看的是,你可以使用 波利 处理这个问题,事实上,他们实现了他们的 同步可取消延迟 这样一来,。
public static Action<TimeSpan, CancellationToken> Sleep = (timeSpan, cancellationToken) =>
{
if (cancellationToken.WaitHandle.WaitOne(timeSpan))
cancellationToken.ThrowIfCancellationRequested();
};