leechw9
1
C# Task.GetAwaiter and GetResult with Application.OnExit(Window.Closing)
์ด์ฒ ์ฐ
C# WPF ์ฑ์ด ์ข
๋ฃํ ๋ ์ด๋ค System.Threading.Tasks.Task๋ก ๋ง๋ฌด๋ฆฌ ํ๋ ค๊ณ ํ๋ค๋ฉด, async/await ๊ตฌ๋ฌธ ๋์ ์ Task.GetAwaiter()์ GetResult()๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ์ข
๋ฃ ์ด๋ฒคํธ๋ก ์ ์ ํ ๊ฒ์ Application.OnExit(๋๋ Window.Closing) ์
๋๋ค.
Visual Studio 2022 Community๋ฅผ ์ด์ฉํ์ฌ .Net 9 WPF ์ฑ ํ๋ก์ ํธ๋ฅผ ๋ง๋ญ๋๋ค.
App.xaml ํ์ผ์์ StartupUri=โMainWindow.xamlโ ๋ฅผ ์ง์๋๋ค.
// App.xaml
<Application x:Class="Study.Wpf.TestGetAwaiter.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Study.Wpf.TestGetAwaiter">
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs ์์ ํด๋์ค App์ ์๋์ฒ๋ผ ๋ฐ๊ฟ๋๋ค. ์ฌ๊ธฐ์ Task.Delay๊ฐ ๋ง๋ฌด๋ฆฌ ์์
์ ๋์ ํฉ๋๋ค.
// App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
AllocConsole();
Console.WriteLine(nameof(OnStartup));
MainWindow = new MainWindow();
MainWindow.Show();
}
protected override void OnExit(ExitEventArgs e)
{
Task.Delay(3000).GetAwaiter().GetResult();
Console.WriteLine(nameof(Task.Delay));
Console.ReadLine();
Console.WriteLine(nameof(OnExit));
FreeConsole();
base.OnExit(e);
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool FreeConsole();
}
์ด๊ฒ์ ๋น๋ํ๊ณ ์คํํ๊ณ ์ข
๋ฃํ๋ฉด, 3์ด ๋ค์ 'Delayโ๊ฐ ์ถ๋ ฅ๋จ(๋ง๋ฌด๋ฆฌ)์ ์ ์ ์์ต๋๋ค.
Task.Delay(3000).GetAwaiter().GetResult(); ๋์ ์ ์๋์ฒ๋ผ
await Task.Delay(3000).ConfigureAwait(false); ๋ฅผ ํ์ฉํ๋ฉด 'Delayโ๊ฐ ์ถ๋ ฅ๋์ง ์์ต๋๋ค.
protected override async void OnExit(ExitEventArgs e)
{
//Task.Delay(3000).GetAwaiter().GetResult();
await Task.Delay(3000).ConfigureAwait(false);
Console.WriteLine(nameof(Task.Delay));
Console.ReadLine();
Console.WriteLine(nameof(OnExit));
FreeConsole();
base.OnExit(e);
}
๋น๋๋น ํ๋ก๊ทธ๋จ์์ ๋๋๋ก ์ฌ์ฉํ์ง ๋ง๋ผ๋ Task.GetAwaiter()์ GetResult()๊ฐ ์ ์ํฉ์์ ์ค์ํ ์ญํ ์ ํฉ๋๋ค.
4๊ฐ์ ์ข์์
await ํด๋ฒ๋ฆฌ๋ฉด ์ง์
๋ฉ์๋๋ฅผ ํธ์ถํ ์ธก์์ ๋ฐ๋ก ๋ฆฌํด๋์ด ๋ฒ๋ฆฌ๊ณ
์ดํ ๊ณง์ด์ด ์ฑ ์ข
๋ฃ ์ ์ฐจ๊ฐ ์งํ๋๋ฉด์ ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ await ํ ๊ฑฐ๋ณด๋ค ๋จผ์ ์ฑ์ด ์ข
๋ฃ๋๋ ์ํฉ ๊ฐ์ ๋ณด์ด๋ค์.
์คํ๋ ค blocking ์ด ํ์ํ ์ํฉ์ด๋ผ ์ด๋ ๊ฒ ํ๋ ๊ฒ ์๋ฏธ ์๊ฒ ๋ค์.
์๊ฐ ๋ชปํ๋ ์ํฉ์ธ๋ฐ ์๊ฑด ์ ์ฉํ ๊ฑฐ ๊ฐ์์.
๋ค๋ง ์ด๋ฐ ๋ถ์์ฉ(?) ๋ฑ์ ๊ณ ๋ คํ๋ฉด
์ข
๋ฃ์ ์ฐจ๋ ๋น๋๊ธฐ๋ฅผ ์ ํ๋๊ฒ ์ ์ ๊ฑด๊ฐ์ด ์ด๋กญ์ต๋๋ค.. =ใ
=!
5๊ฐ์ ์ข์์
al6uiz
3
์ ์ด๋ SynchronizationContext๊ฐ Dispatcher์ธ WPF ํ๊ฒฝ์์๋ ์ฌ๋ ค์ฃผ์ ๋ด์ฉ์ด ๋ฐ๋์งํ ๋ฐฉํฅ์ ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
OnExit ํจ์ ๋ด์์ ๋๊ธฐ ์ค์ธ Task ์์์ ๋ ๋ค๋ฅธ Task๋ฅผ awaitํ๋ ๊ฒฝ์ฐ ๋ฐ๋๋ฝ์ ์ ๋ฐํ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค.
private async Task ReleaseAsync()
{
Console.WriteLine("D1");
await Task.Delay(10);
Console.WriteLine("D2");
}
์์ ์ฝ๋๋ ConfigureAwait(false) ์์ด await๊ฐ ์ปจํ
์คํธ๋ฅผ ์บก์ฒํ ์ํ๋ก ์ง์
ํ๊ธฐ ๋๋ฌธ์ Dispatcher๊ฐ ๋ค์ ํํธ๋ฅผ ์คํํ ์ ์์ ๋๊น์ง ๋๊ธฐํ๊ฒ ๋ฉ๋๋ค.
protected override void OnExit(ExitEventArgs e)
{
ReleaseAsync().GetAwaiter().GetResult();
...
์ฌ๋ ค์ฃผ์ ๋ฐฉ์๋๋ก๋ผ๋ฉด Dispatcher๊ฐ OnExit ๋ด๋ถ์์ ๋๊ธฐํ๊ณ ์์ผ๋ฏ๋ก D2 ํํธ๋ฅผ ์คํํ ์ ์๊ฒ ๋๋ฉฐ, ๊ฒฐ๊ณผ์ ์ผ๋ก ๋ฐ๋๋ฝ ์ํ์ ๋น ์ง๊ฒ ๋ฉ๋๋ค.
WPF์์ ๋น๋๊ธฐ ์ข
๋ฃ ๋ก์ง์ ์ํํ๋ ๊ฒฝ์ฐ์๋ ShutdownMode๋ฅผ OnExplicitShutdown ๋๋ OnMainWindowClose๋ก ์ค์ ํ ๋ค,
์๋์ ๊ฐ์ด ๋ฉ์ธ ์๋์ฐ๊ฐ ๋ซํ ๋ ๋ชจ๋ ๋น๋๊ธฐ ์์
์ ์๋ฃํ ํ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ข
๋ฃ๋๋๋ก ๊ตฌ์ฑํ๋ ๋ฐฉ์์ด ๊ฐ์ฅ ์์ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
OnMainWindowClose ์ผ ๊ฒฝ์ฐ
public partial class MainWindow : Window
{
private bool _isClosing;
protected override async void OnClosing(CancelEventArgs e)
{
if (_isClosing)
return;
e.Cancel = true; // await ์ํ์ Cancel ์ค์
_isClosing = true;
try
{
await CleanupAsync();
}
catch (Exception ex) { } // ์ค๋ฅ ์ฒ๋ฆฌ
finally
{
Close(); // ๋ค์ ํธ์ถ
}
}
private async Task CleanupAsync()
{
// ๋น๋๊ธฐ ์ข
๋ฃ ์์
ํธ์ถ
await SomeTask;
}
}
OnExplicitShutdown ์ผ ๊ฒฝ์ฐ
...
MainWindow.Closed += Handler_MainWindowClosed;
}
private async void Handler_MainWindowClosed(object? sender, EventArgs e)
{
try
{
await CleanupAsync();
}
catch (Exception ex) { } // ์ค๋ฅ ์ฒ๋ฆฌ
finally
{
Application.Current.Shutdown();
}
}
private async Task CleanupAsync()
{
// ๋น๋๊ธฐ ์ข
๋ฃ ์์
ํธ์ถ
await SomeTask;
}
}
4๊ฐ์ ์ข์์
leechw9
4
์ฃผ์ํ ์ ์
- ํ๋ก์ ํธ ์์ฑ์ Output type : Windows Application
- ShutdownMode๋ OnExplicitShutdown๊ฐ ์๋ ๋.
[AllocConsole();๊ณผ FreeConsole(); ๋ฅผ ์ฌ์ฉํ์ฌ ์ถ๋ ฅ]
- ๊ด์ฌ์ฌ ๋ถ๋ฆฌ : ํด๋์ค Application์์ ์์, ์ข
๋ฃ ์์
์ ์ํ.
์์ฐ์ค๋ฐ ๋ง๋ฌด๋ฆฌ ์์
(Task)์ ์ํํ๋ ๊ฒ์
๋๋ค.
๊ทธ๋ฆฌ๊ณ Window.Closing ์ด๋ฒคํธ๋ฅผ ์ฐ๋ ค๋ฉด, ์๋์ ๊ฐ์ด ํฉ๋๋ค.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
AllocConsole();
Console.WriteLine(nameof(OnStartup));
MainWindow = new MainWindow();
MainWindow.Closing += MainWindow_Closing;
MainWindow.Show();
}
private void MainWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e)
{
MainWindow.Closing -= MainWindow_Closing;
Task.Delay(3000).GetAwaiter().GetResult();
Console.WriteLine(nameof(Task.Delay));
}
protected override void OnExit(ExitEventArgs e)
{
//Task.Delay(3000).GetAwaiter().GetResult();
//Console.WriteLine(nameof(Task.Delay));
Console.ReadLine();
Console.WriteLine(nameof(OnExit));
FreeConsole();
base.OnExit(e);
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool FreeConsole();
}