有时我会有一个类在后台执行一些异步操作,通常是网络或其他 IO 操作。我希望它在调用
Dispose()
时停止并清理,但如果以某种方式从后台任务本身调用 Dispose()
,这种模式似乎可能会导致死锁(我怀疑普通线程可能会出现类似的缺陷,如好吧)。
TaskCompletionSource.SetResult
等可能会导致某些外部
await
继续在运行后台任务的线程上运行,然后调用dispose。
Dispose()
函数等待后台内容完成并使用其他方法来确保一切在从 main 返回之前停止?
Dispose()
的代码的错误,让它从“回调”中发生(对于传统线程,似乎许多库都会这么说,但
await foo
潜在的上下文切换使线程更难控制)?
Action
/
Func
、
Subject<T>.OnNext()
、
TaskCompletionSource.SetResult()
等)?
BackgroundTask
类似于 WebSocket 或其他网络服务,会一直挂起,直到应用程序关闭。
public sealed class BackgroundTask : IDisposable
{
private readonly CancellationTokenSource _cts = new();
private readonly Task _task;
// Probably some sort of message queue
private TaskCompletionSource? _thingToDoTcs;
public BackgroundTask()
{
_task = TaskMainAsync(_cts.Token);
}
public void Dispose()
{
_cts.Cancel();
_task.Wait();
}
public Task DoSomethingOnTask()
{
_thingToDoTcs = new();
return _thingToDoTcs.Task;
}
private async Task TaskMainAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(100);
if (_thingToDoTcs != null)
{
Console.WriteLine("Doing a thing...");
await Task.Delay(1000);
_thingToDoTcs.SetResult();
}
// Doing other stuff as well, e.g. a Socket read loop
}
}
static async Task AsyncMain()
{
using var background = new BackgroundTask();
var task = background.DoSomethingOnTask();
Console.WriteLine("Doing other stuff...");
await task; // May continue on a different thread to the caller
Console.WriteLine("Done everything, time to shut down.");
// Can deadlock because `await task` ends up being the thread running TaskMainAsync
// and then Dispose() calls .Wait() on it.
// `using` blocks make implicit on return, exception, etc. so can't fully control call site
background.Dispose();
Console.WriteLine("Disposed");
}
static void Main(string[] args)
{
AsyncMain().Wait();
Console.WriteLine("Exit main");
}
}
IAsyncDisposable
来代替,进行等待处理。请注意使用
await using
而不是普通的
using
。
public sealed class BackgroundTask : IAsyncDisposable
{
......
public ValueTask Dispose()
{
_cts.Cancel();
await _task;
}
......
static async Task AsyncMain()
{
await using var background = new BackgroundTask();
var task = background.DoSomethingOnTask();
Console.WriteLine("Doing other stuff...");
await task; // May continue on a different thread to the caller
Console.WriteLine("Done everything, time to shut down.");
Console.WriteLine("Disposed");
}