非同期処理とApplication.Shutdown()

## はじめに
本記事は*C# WPF.NetFlamework*を対象とした記事となります。非同期処理を実装するシステムにて、適切に非同期処理を破棄する終了処理のベストプラクティスを模索した時の記録(2020/9/13時点)を`個人用`にまとめます。  


# 非同期処理について  
## foreground / background thread 概要
マネージドスレッドは以下の二つに分類されます。  

| 種類 | 説明 |
|:-----------------|:-------------------|
| foreground thread | マネージド実行環境を維持できる。 |
| background thread | マネージド実行環境を維持できない。つまりすべてのフォアグラウンドが停止すると、システムによって全てのバックグラウンドスレッドが停止させられる。 |  
  
これはUIスレッドで非同期処理を実行した場合、非同期処理実行中にUIスレッドを停止したときの動作の違いを意味します。「background thread」はUIスレッド終了に伴い終了しますが、`「foreground thread」は終了しません。`
  
## foreground / background thread 判定方法
次に特定の非同期処理がどちらに該当するのかを確認します。  
方法としては、実際に非同期処理実行中にUIスレッドを停止してみて確認することもできるかもしれませんが、`Thread.IsBackgroundというプロパティを参照する`のが最も容易です。
しかし、Threadオブジェクトを生成せずに非同期処理を実行する場合は、非同期処理内で以下の記述をすることで*Thread.IsBackground*の規定値を確認することができます。  

```
Console.WriteLine(Thread.CurrentThread.IsBackground);
```

ちなみにThreadオブジェクトを生成した際の規定値は*`False`*、Task.Run()にて非同期処理を実行する場合は*`Ture`*が規定値となります。  
# Applicationの終了について  
ここまでUIスレッドと非同期スレットとの関係性についての注意事項を述べましたが、本題であるApplication終了時の関係性について触れていきます。先に結論を言うと、`Applicationが終了するとマネージドスレッドの種類に関係なく、非同期スレッドは終了します。`では、Applicationの終了について詳しく見ていきます。
## Window.Close()  
基本的には、WPFアプリケーションテンプレートで作成した`MainWindow.xaml.csにてClose()メソッドを呼び出すことでApplicationは終了`します。ただしApp.xamlもしくはApp.xaml.csを編集した場合は、それに限りません。  
  
厳密にいうと*Application.MainWindow*に設定された*Window*インスタンスが*Close()*されたときに、*Application.ShutdownMode*が*OnLastWindowClose*に設定されていたかどうかによります。
## Application.ShutdownMode  
*Application.ShutdownMode*が規定値である*OnLastWindowClose*に設定されていた場合、*Application.MainWindow*に設定されたWindowインスタンスの*Close()*時にApplicationは終了されます。しかし`OnExplicitShutdownに設定されていた場合は、Close()＝Applicationの終了とはなりません。`
※結果的にAppicationが終了することはありますが、Applicationが生きたままとなる危険性があります。

したがって後者の場合は、`Close()ではなく明示的にApplication.Shutdown()メソッドを呼び出す必要があります。`
## Application.MainWindow  
ここで*Application.MainWindow*にWindowインスタンスを設定する方法を見ていきます。*Application.MainWindow*にセットするには、`App.xaml`で行う方法と`App.xaml.cs`で行う方法の2通りあります。  
テンプレートからWPFアプリケーションを作成した場合は`App.xaml`のApplicationの*StartupUri*にて記述されています。  

```App.xaml
<Application x:Class="HelloWorld.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>
```

続いて、App.xaml.csでMainWindowを設定する方法を提示します。

```App.xaml
<Application x:Class="HelloWorld.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Startup="AppStartup">
    <Application.Resources>

    </Application.Resources>
</Application>
```

```App.xaml.cs
public partial class App : Application
{
    void AppStartup(object sender, StartupEventArgs e)
    {
        MainWindow window = new MainWindow();
        window.Show();
    }
}
```
コチラの方法では`App.xaml`の*StartupUri*にて直接xamlを指定せずに、`App.xaml.cs`のStartupイベント上にてWindowインスタンスを生成します。　　
　　
<div><font color="gray">ここで補足になりますが、上記では*Application.MainWindow*に明示的にセットしていません。しかし、実は*Application.Mainwindow*に生成したMainWindowsインスタンスがセットされています。これは、最初にWindowsインスタンスを生成した際に自動でそのインスタンスが*Application.MainWindow*にセットされる仕様のため、上記のような記述になっています。  
したがって同イベント内でWindowインスタンスを2つ生成した場合は、最初に生成したWindowインスタンスが*Close()*した段階で*Application*が終了処理に入るので、以降の処理は実行されないことに注意してください。</font></div>

```App.xaml.cs
public partial class App : Application
{
    void App_Startup(object sender, StartupEventArgs e)
    {
        // Windowsインスタンスを生成した際に自動でそのインスタンスが
        // Application.MainWindowにセットされる。
        Window firstWindow = new MainWindow();
        Window secondWindow = new MainWindow();
        
        firstWindow.ShowDialog();
        // 移行の処理は実行されません。
        secondWindow.ShowDialog();
    }
}
```

# まとめ

以上より、非同期処理を実装したアプリケーションは、以下に留意して実装することを結論としました。

* 非同期処理は理由のない限り、*「background thread」*で処理させる。
* マネージドスレッドの種類を問わず、Applicationを終了させることで非同期スレッドも終了させることができる。
* *Application.ShutdownMode*や*Appication.MainWindow*が編集されることを考慮して、基本的には*Close()*ではなく明示的に*Application.ShutDown()*を使用してApplication終了処理を記述する。

<div><font color=gray>※ガベージコレクションについては話がややこしくなるので、ココでは触れない。</font><div>