TaskにConfigureAwait(false)を付けると、それ以降の処理はそれ以前のスレッドとは別のスレッドで行われることになる

やりたいこと

Taskでawaitしている個所に.ConfigureAwait(false)を付けると、その後の処理で、アプリが落ちたりすることがある。なんでなのか?調べたい。

わかったこと

.ConfigureAwait(false)を付けたときとつけてないときで、処理が行われるスレッドが違う。

コードで見る

おちない場合

下記のようなコードがあるとして、

using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp2
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            DispThread(1);

            await Task.Run(async () =>
            {
                DispThread(2);
            });//.ConfigureAwait(false);     // ★この「.ConfigureAwait(false);」

            DispThread(3);

            bt.Content = "abc";
        }

        private void DispThread(int id)
        {
            Debug.WriteLine($" {id}:{Thread.CurrentThread.ManagedThreadId}");
        }
    }
}

つけてないと、こうなる。

開始冒頭:スレッドID=1
タスク内:スレッドID=9
タスク後:スレッドID=1

※スレッドIDは、実行都度異なるかもしれない。

タスク後は、UIのスレッドに戻るので、普通に画面更新の処理bt.Content = "abc";ができる。

落ちる場合

private async void Button_Click(object sender, RoutedEventArgs e)
{
    DispThread(1);

    await Task.Run(async () =>
    {
        DispThread(2);
    }).ConfigureAwait(false);    // ★この「.ConfigureAwait(false);」

    DispThread(3);

    bt.Content = "abc";
}

これを実行すると、下記の例外で落ちる。

この場合のスレッド番号は下記になる。

開始冒頭:スレッドID=1
タスク内:スレッドID=9
タスク後:スレッドID=9

で、なぜ落ちるかというと、UIの更新は、UIのスレッド(スレッドID=1)でしか行えず、別のスレッドで行うと、上記の例外が起きる。

タスクを.ConfigureAwait(false);すると、UIスレッドに戻らずに、Taskを実行したままのスレッドで続きをやるようになるので、UI更新を含むタスク後のスレッドが、UIスレッドではないスレッドで実行されるようになり、落ちる。