使用和等待使用有什么区别?我如何决定使用哪一个



我注意到,在某些情况下,Visual Studio建议执行此

await using var disposable = new Disposable();
// Do something

而不是这个

using var disposable = new Disposable();
// Do something

usingawait using之间有什么区别?

我应该如何决定使用哪一个?

经典同步使用

Classic使用调用实现IDisposable接口的对象的Dispose()方法。

using var disposable = new Disposable();
// Do Something...

相当于

IDisposable disposable = new Disposable();
try
{
// Do Something...
}
finally
{
disposable.Dispose();
}

新的异步等待使用

新的等待使用调用和等待实现IAsyncDisposable接口的对象的DisposeAsync()方法。

await using var disposable = new AsyncDisposable();
// Do Something...

相当于

IAsyncDisposable disposable = new AsyncDisposable();
try
{
// Do Something...
}
finally
{
await disposable.DisposeAsync();
}

.NET Core 3.0.NET Standard 2.1中添加了IAsyncDisposable接口。

In。NET中,拥有非托管资源的类通常实现IDisposable接口,以提供同步释放非托管资源。但是,在某些情况下,除了同步机制之外,它们还需要提供一种异步机制来释放非托管资源。提供这样的机制使得消费者能够在不长时间阻塞GUI应用程序的主线程的情况下执行资源密集型处置操作。

IAsyncDisposable。此接口的DisposeSync方法返回一个ValueTask,该ValueTask表示异步处置操作。拥有非托管资源的类实现了此方法,当不再需要对象时,这些类的使用者会在对象上调用此方法。

Justin Lessard的回答解释了usingawait using之间的区别,所以我将重点讨论使用哪一个。有两种情况:要么Dispose/DisposeAsync这两种方法是互补的,要么它们在做一些不同的事情。
  1. 如果方法是互补的(这是常见的情况(,则可以调用其中任何一个方法,结果将是相同的:非托管资源将被释放。没有理由同时按顺序调用它们。如果你这样做了,第二个调用将是no op:资源已经释放,所以没有什么可做的了。选择调用哪一个很容易:如果你在同步上下文中,则调用Dispose()(使用using(。如果您处于异步上下文中,请调用await DisposeAsync()(使用await using(。

  2. 如果这些方法正在做一些不同的事情,您应该阅读文档,并决定哪种行为更适合手头的场景。让我们以实现两个接口(IDisposableIAsyncDisposable(的System.Threading.Timer类为例进行讨论。Dispose方法按预期释放非托管资源,但DisposeAsync所做的远不止这些:它还等待当前正在运行的任何回调的完成。让我们做一个实验来证明这种差异:

var stopwatch = Stopwatch.StartNew();
using (new Timer(_ => Thread.Sleep(1000), null, 0, Timeout.Infinite))
{
Thread.Sleep(100);
}
Console.WriteLine($"Duration: {stopwatch.ElapsedMilliseconds:#,0} msec");

我们创建了一个在0毫秒后触发的计时器,所以实际上是立即触发的,然后我们等待100毫秒以确保回调已经被调用(它是在ThreadPool线程上调用的(,然后我们同步处理计时器。这是这个实验的结果:

Duration: 102 msec

现在让我们从using切换到await using。这是第二个实验的结果:

Duration: 1,005 msec

DisposeAsync的隐式调用返回了一个仅在计时器回调完成后才完成的ValueTask

因此,对于Timer,在usingawait using之间进行选择不仅仅是取决于上下文的选择。您可能更喜欢在异步上下文中使用同步using,因为您不关心回调(您知道让它变成即发即弃是无害的(。或者,您可能处于同步上下文中,但您可能更喜欢await using的行为,因为即发即弃是不可接受的。在这种情况下,您将不得不放弃using的便利性,而是在finally块中显式调用DisposeAsync

var timer = new Timer(_ => Thread.Sleep(1000), null, 0, Timeout.Infinite);
try { Thread.Sleep(100); }
finally { timer.DisposeAsync().AsTask().Wait(); }

请注意,尤其是在编写库时,await using默认情况下会捕获同步上下文。如果这是不需要的,通常是针对库代码,则必须使用ConfigureAwait(false)对其进行配置。这有一些在这里讨论的含义:;等待使用";语法正确吗

相关内容

  • 没有找到相关文章

最新更新