我注意到在某些情况下,Visual Studio建议这样做
await using var disposable = new Disposable();
// Do something
Run Code Online (Sandbox Code Playgroud)
代替这个
using var disposable = new Disposable();
// Do something
Run Code Online (Sandbox Code Playgroud)
using和之间有什么区别await using?
我应该如何决定使用哪个?
The*_*ias 30
Justin Lessard 的回答using解释了和之间的区别await using,因此我将重点讨论使用哪一个。有两种情况:要么这两种方法Dispose是DisposeAsync互补的,要么它们在做不同的事情。
如果这些方法是互补的(这是常见情况),您可以调用其中任何一个,结果将是相同的:非托管资源将被释放。没有理由依次调用它们两个。如果这样做,第二次调用将是无操作:资源已被释放,因此将不再需要执行任何操作。选择要调用的方法很容易:如果您处于同步上下文中,请调用Dispose()(使用using)。如果您处于异步上下文中,请调用await DisposeAsync()(使用await using)\xc2\xb9。
如果这些方法执行不同的操作,您应该阅读文档并决定哪种行为更适合当前的场景。例如,让我们讨论一下System.Threading.Timer实现两个接口(IDisposable和IAsyncDisposable)的类。该Dispose方法按预期释放非托管资源,但它DisposeAsync所做的还不止于此:它还等待当前正在运行的任何回调的完成。让我们做一个实验来证明这种差异:
var stopwatch = Stopwatch.StartNew();\nusing (new Timer(_ => Thread.Sleep(1000), null, 0, Timeout.Infinite))\n{\n Thread.Sleep(100);\n}\nConsole.WriteLine($"Duration: {stopwatch.ElapsedMilliseconds:#,0} msec");\nRun Code Online (Sandbox Code Playgroud)\n我们创建一个在 0 毫秒后触发的计时器,实际上是立即触发,然后我们等待 100 毫秒以确保回调已被调用(它在线程上调用ThreadPool),然后我们同步处置计时器。这是该实验的输出:
var stopwatch = Stopwatch.StartNew();\nusing (new Timer(_ => Thread.Sleep(1000), null, 0, Timeout.Infinite))\n{\n Thread.Sleep(100);\n}\nConsole.WriteLine($"Duration: {stopwatch.ElapsedMilliseconds:#,0} msec");\nRun Code Online (Sandbox Code Playgroud)\n现在让我们从 切换using到await using。这是第二个实验的输出:
Duration: 102 msec\nRun Code Online (Sandbox Code Playgroud)\nDisposeAsync对返回的隐式调用ValueTask仅在计时器回调完成后才完成。
因此,在 a 的情况下,在和 之间Timer进行选择不仅仅是一个取决于上下文的选择。您可能更喜欢在异步上下文中使用同步,因为您不关心回调(您知道让它变得“即发即忘”并没有什么害处)。或者您可能处于同步上下文中,但您可能更喜欢 的行为,因为“即发即弃”是不可接受的。在这种情况下,您将不得不放弃 , 的便利性,并在块中显式调用:usingawait usingusingawait usingusingDisposeAsyncfinally
var timer = new Timer(_ => Thread.Sleep(1000), null, 0, Timeout.Infinite);\ntry { Thread.Sleep(100); }\nfinally { timer.DisposeAsync().AsTask().Wait(); }\nRun Code Online (Sandbox Code Playgroud)\n\xc2\xb9请注意,特别是如果您正在编写库,await using默认情况下会捕获同步上下文。如果这是不需要的(通常是针对库代码),您必须使用ConfigureAwait(false). 这有一些含义,这里讨论:How do I get the “await using” 语法正确?
Jus*_*ard 13
Classic using调用Dispose()实现IDisposable接口的对象的方法。
using var disposable = new Disposable();
// Do Something...
Run Code Online (Sandbox Code Playgroud)
相当于
IDisposable disposable = new Disposable();
try
{
// Do Something...
}
finally
{
disposable.Dispose();
}
Run Code Online (Sandbox Code Playgroud)
新的等待使用调用,并等待DisposeAsync()实现IAsyncDisposable接口的对象的方法。
await using var disposable = new AsyncDisposable();
// Do Something...
Run Code Online (Sandbox Code Playgroud)
相当于
IAsyncDisposable disposable = new AsyncDisposable();
try
{
// Do Something...
}
finally
{
await disposable.DisposeAsync();
}
Run Code Online (Sandbox Code Playgroud)
该IAsyncDisposable接口在加.NET Core 3.0和.NET Standard 2.1。
在.NET中,拥有非托管资源的类通常实现IDisposable接口,以提供同步释放非托管资源的机制。但是,在某些情况下,除了(或代替)同步资源之外,他们还需要提供一种异步机制来释放非托管资源。提供这种机制使使用者能够执行资源密集型的处置操作,而不会长时间阻塞GUI应用程序的主线程。
此接口的IAsyncDisposable.DisposeAsync方法返回一个ValueTask,它表示异步处置操作。拥有非托管资源的类实现此方法,并且这些类的使用者在不再需要该对象时在对象上调用此方法。