我有一个类,它使用Task.Run
生成几个工作任务,并在集合中引用这些任务。此外,这个类实现IDisposable
以便清理。在Dispose()
的实施中,我使用Task.WaitAll(_listOfTasks)
等待所有工人完成。
现在可能发生的事情是,对Dispose()
的调用是来自其中一个工作任务,这显然会导致自Qazxswpoi等待自己的死锁。
是否有模式或推荐的方法来解决这种情况?或者还有其他方法可以确保在处理类时完成所有正在运行的任务吗?
WaitAll
有几种方法可以在.NET生态系统中使用public class Loader : IDisposable
{
private readonly IList<Task> _runningTasks = new List<Task>();
public Loader()
{
}
public void Dispose()
{
Task.WaitAll(_runningTasks.ToArray());
}
public void StartLoadAsync()
{
var task = Task.Run(() => DoSomeWork());
_runningTasks.Add(task);
}
void DoSomeWork()
{
// after doing some actual work here, call Dispose() in certain cases
if (SomeCondition)
{
Dispose();
}
}
}
接口。我想展示其中两个在多线程/异步使用中特别相关的内容。
订阅一个observable时,会返回一个IDisposable
对象。这里接口用作取消触发器。没有线程关联,可以随时调用它。实施承诺尽力取消,但在取消最终发生时不予以保证。这意味着在IDisposable
调用之后,订阅可能仍然有效一段时间。
Dispose
在即将推出的IAsyncEnumerator
界面中,您将能够在检索枚举器时传递取消令牌。枚举器实现IAsyncEnumerable
,应予以处置。枚举器不需要是线程安全的,即,当对另一个接口方法或返回的任务的调用仍在运行时,不允许调用IAsyncDisposable
。如果要停止枚举,则必须使用取消令牌。
区分取消和资源清理很重要。在您的情况下,您还可以使用取消令牌。由于您已经拥有围绕方法调用的所有内容,您可以选择添加DisposeAsync
方法。如果你现在需要加载器实际终止,你可以实现Cancel
接口:
IAsyncDisposable
如果你愿意,你可以从public void Cancel() => ...;
public async ValueTask DisposeAsync()
{
// Cancel();
await Task.WhenAll(_runningTasks.ToArray());
}
方法调用Cancel
。我不确定是否有任何最佳实践。我倾向于不在那里调用DisposeAsync
,因为它为您或您的API用户提供了更多可能性。请注意,Cancel
界面只有netstandard 2.1。但是,这不应该阻止你现在使用这种模式。