【发布时间】:2019-04-14 16:19:22
【问题描述】:
我一直在熟悉 C# 8 和 .NET Core 3.0 中添加的一些东西(计划添加),并且不确定实现 IAsyncDisposable 的正确方法(在撰写本文时,这链接实际上没有指导)。
特别是,我不清楚在没有显式处置实例的情况下该怎么做 - 也就是说,它没有被包裹在 async using(...) 中并且 .DisposeAsync() 没有被显式调用。
我的第一个想法是做与实现IDisposable 时相同的事情:
- 我的
DisposeAsync()实现调用DisposeAsync(bool disposing)和disposing: true - 实现一个调用
DisposeAsync(disposing: false)的终结器(使用~MyType()) -
DisposeAsync(bool disposing)实际上释放和/或处置所有内容,如果disposing == true则禁止最终确定。
我担心在终结器中没有什么可以等待DisposeAsync(bool) 的结果,而在终结器中显式等待似乎真的很危险。
当然,“只是泄漏”似乎也不太理想。
为了具体说明,这里是一个(简化的)示例类,确实有一个终结器:
internal sealed class TestAsyncReader: IAsyncDisposable
{
private bool IsDisposed => Inner == null;
private TextReader Inner;
internal TestAsyncReader(TextReader inner)
{
Inner = inner;
}
// the question is, should this exist?
~TestAsyncReader()
{
DisposeAsync(disposing: false);
}
private ValueTask DisposeAsync(bool disposing)
{
// double dispose is legal, but try and do nothing anyway
if (IsDisposed)
{
return default;
}
// should a finalizer even exist?
if (disposing)
{
GC.SuppressFinalize(this);
}
// in real code some resources explicitly implement IAsyncDisposable,
// but for illustration purposes this code has an interface test
if (Inner is IAsyncDisposable supportsAsync)
{
var ret = supportsAsync.DisposeAsync();
Inner = null;
return ret;
}
// dispose synchronously, which is uninteresting
Inner.Dispose();
Inner = null;
return default;
}
public ValueTask DisposeAsync()
=> DisposeAsync(disposing: true);
}
那么,有没有关于正确处理泄露的IAsyncDisposable 实例的指导?
【问题讨论】:
-
Here's the commit 将
IAsyncDisposable添加到Threading.Timer。不知道这是否有帮助,但这是其实施的一个例子...... -
Dispose 做了两件事:释放托管资源和释放非托管资源。我可以想象需要异步释放托管资源(例如,需要正常关闭的数据库连接),但我很难想到需要异步释放的非托管资源(内存、文件指针等)。对于任何托管资源,要么它需要被显式释放(例如事件订阅),要么理想情况下它应该被释放但它的终结器可以处理它,如果它不是 - 无论哪种方式,你的终结者不应参与其中。
-
顺便说一句,尽量避免编写自己的终结器。它使您的对象分配成本更高,并且很容易搞砸(例如,大多数人不知道您的可终结对象可以在调用本机方法时被终结,这会导致各种问题)。使用
SafeHandle及其派生类更容易、更安全。这回避了你的问题。 -
Dispose(bool disposing)是一个可怕的模式。为了所有神圣的爱,我们不要对DisposeAsync做同样的事情。 -
@bboyle1234:我have always recommended 仅对包装非托管资源的类型使用终结器。这些类型应该只有一个同步的
Dispose。如果您遵循该模式,则永远不需要将终结器与DisposeAsync集成。
标签: c# asynchronous dispose