参考
こちらの記事を参考にさせていただきました
目的
usingの後にawaitを呼んだ場合に、対応するDisposeはどのスレッドで呼ばれるのか確認しました
結果
- ConfigureAwait(false)をつけないでawaitした場合、usingを呼んだスレッドに戻ってからDisposeが呼ばれた
- ConfigureAwait(false)をつけてawaitした場合、usingを呼んだスレッドに戻らずにDisposeが呼ばれた
- using宣言と、スコープを指定するusing、usingと同等のtry&finallyの置き換えで同様の動作となった
確認用コード
MainWindow.xaml.cs
using System.Diagnostics;
using System.Windows;
namespace WpfApp2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public static class DebugLogger
{
public static void LogDebug(string message)
{
int threadId = Thread.CurrentThread.ManagedThreadId;
Debug.WriteLine($"[Thread {threadId}] {message}");
}
}
public class MyData : IDisposable
{
public string Data { get; set; } = "Initial Data";
public MyData()
{
DebugLogger.LogDebug("MyData Constructor Called");
}
public void Dispose()
{
DebugLogger.LogDebug("MyData Disposed");
}
}
public MainWindow()
{
InitializeComponent();
}
public async Task ProcessDataAsync(MyData data)
{
await Task.Run(() =>
{
DebugLogger.LogDebug("Processing data...");
Thread.Sleep(1000);
data.Data = "Processed Data";
DebugLogger.LogDebug($"Data updated to: {data.Data}");
});
}
private async void Button1_Click(object sender, RoutedEventArgs e)
{
DebugLogger.LogDebug("//-----------------------");
DebugLogger.LogDebug("Button1_Click Starting...");
using var myData = new MyData();
await ProcessDataAsync(myData);
DebugLogger.LogDebug("Button1_Click Finished.");
}
private async void Button2_Click(object sender, RoutedEventArgs e)
{
DebugLogger.LogDebug("//-----------------------");
DebugLogger.LogDebug("Button2_Click Starting...");
using var myData = new MyData();
DebugLogger.LogDebug("ProcessDataAsync Starting...");
await ProcessDataAsync(myData).ConfigureAwait(false);
DebugLogger.LogDebug("Button2_Click Finished.");
}
private async void Button3_Click(object sender, RoutedEventArgs e)
{
DebugLogger.LogDebug("//-----------------------");
DebugLogger.LogDebug("Button3_Click Starting...");
using (var myData = new MyData())
{
await ProcessDataAsync(myData);
}
DebugLogger.LogDebug("Button3_Click Finished.");
}
private async void Button4_Click(object sender, RoutedEventArgs e)
{
DebugLogger.LogDebug("//-----------------------");
DebugLogger.LogDebug("Button4_Click Starting...");
using (var myData = new MyData())
{
await ProcessDataAsync(myData).ConfigureAwait(false);
}
DebugLogger.LogDebug("Button4_Click Finished.");
}
private async void Button5_Click(object sender, RoutedEventArgs e)
{
DebugLogger.LogDebug("//-----------------------");
DebugLogger.LogDebug("Button5_Click Starting...");
MyData? myData = null;
try
{
myData = new MyData();
DebugLogger.LogDebug("ProcessDataAsync Starting...");
await ProcessDataAsync(myData);
DebugLogger.LogDebug("ProcessDataAsync Finished.");
}
catch (Exception ex)
{
DebugLogger.LogDebug($"An error occurred: {ex.Message}");
}
finally
{
if (myData != null)
{
myData.Dispose();
DebugLogger.LogDebug("MyData disposed in finally block.");
}
DebugLogger.LogDebug("Button5_Click Finished.");
}
}
private async void Button6_Click(object sender, RoutedEventArgs e)
{
DebugLogger.LogDebug("//-----------------------");
DebugLogger.LogDebug("Button6_Click Starting...");
MyData? myData = null;
try
{
myData = new MyData();
DebugLogger.LogDebug("ProcessDataAsync Starting...");
await ProcessDataAsync(myData).ConfigureAwait(false);
DebugLogger.LogDebug("ProcessDataAsync Finished.");
}
catch (Exception ex)
{
DebugLogger.LogDebug($"An error occurred: {ex.Message}");
}
finally
{
if (myData != null)
{
myData.Dispose();
DebugLogger.LogDebug("MyData disposed in finally block.");
}
DebugLogger.LogDebug("Button6_Click Finished.");
}
}
}
}
出力結果
[Thread 1] //-----------------------
[Thread 1] Button1_Click Starting...
[Thread 1] MyData Constructor Called
[Thread 15] Processing data...
[Thread 15] Data updated to: Processed Data
[Thread 1] Button1_Click Finished.
[Thread 1] MyData Disposed
[Thread 1] //-----------------------
[Thread 1] Button2_Click Starting...
[Thread 1] MyData Constructor Called
[Thread 1] ProcessDataAsync Starting...
[Thread 3] Processing data...
[Thread 3] Data updated to: Processed Data
[Thread 3] Button2_Click Finished.
[Thread 3] MyData Disposed
[Thread 1] //-----------------------
[Thread 1] Button3_Click Starting...
[Thread 1] MyData Constructor Called
[Thread 3] Processing data...
[Thread 3] Data updated to: Processed Data
[Thread 1] MyData Disposed
[Thread 1] Button3_Click Finished.
[Thread 1] //-----------------------
[Thread 1] Button4_Click Starting...
[Thread 1] MyData Constructor Called
[Thread 3] Processing data...
[Thread 3] Data updated to: Processed Data
[Thread 3] MyData Disposed
[Thread 3] Button4_Click Finished.
[Thread 1] //-----------------------
[Thread 1] Button5_Click Starting...
[Thread 1] MyData Constructor Called
[Thread 1] ProcessDataAsync Starting...
[Thread 3] Processing data...
[Thread 3] Data updated to: Processed Data
[Thread 1] ProcessDataAsync Finished.
[Thread 1] MyData Disposed
[Thread 1] MyData disposed in finally block.
[Thread 1] Button5_Click Finished.
[Thread 1] //-----------------------
[Thread 1] Button6_Click Starting...
[Thread 1] MyData Constructor Called
[Thread 1] ProcessDataAsync Starting...
[Thread 15] Processing data...
[Thread 15] Data updated to: Processed Data
[Thread 15] ProcessDataAsync Finished.
[Thread 15] MyData Disposed
[Thread 15] MyData disposed in finally block.
[Thread 15] Button6_Click Finished.