86296


C# の Windows Forms アプリケーションにおいて、Windows Forms のイベントで async メソッドを await
で待機した場合は、async メソッド内の await 前後でスレッドIDが変化しませんが、Main メソッドで async メソッドを await
で待機した場合は、async メソッド内の await 前後でスレッドIDが変化します。

Main メソッドから async メソッドを実行した場合も async メソッド内の await
前後でスレッドIDを変化させないようにするにはどのようにすればよいでしょうか？

尚、Main メソッド内で `task.Wait()` を使用した場合はスレッドIDが変化しないことが確認できました。  
そもそも Main メソッド内で await で async メソッドを待つという考え自体が間違っているのでしょうか？  
説明を頂ければありがたいです。

### 例

#### Program.cs

[code]

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace WindowsFormsAppTestAwait
    {
        internal static class Program
        {
            [STAThread]
            private static async Task Main()
            {
                // スレッドID変化あり
                await HeavyMethodAsync("Main");
    
                // スレッドID変化なし
                HeavyMethodAsync("Main").Wait();
    
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
    
            internal static Task HeavyMethodAsync(string name)
            {
                return Task.Run(async () =>
                {
                    Console.WriteLine($"{name}#{Thread.CurrentThread.ManagedThreadId}: before");
                    await Task.Delay(1000);
                    Console.WriteLine($"{name}#{Thread.CurrentThread.ManagedThreadId}: after");
                });
            }
        }
    }
    
[/code]

#### Form1.cs

※Form1 の Load イベントで `HeavyMethodAsync` を呼んでいます

[code]

    using System;
    using System.Windows.Forms;
    
    namespace WindowsFormsAppTestAwait
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private async void Form1_Load(object sender, EventArgs e)
            {
                // スレッドID変化なし
                await Program.HeavyMethodAsync("Form");
            }
        }
    }
    
[/code]

### 結果

[code]

    Main#3: before
    Main#4: after
    Main#3: before
    Main#3: after
    Form#3: before
    Form#3: after
    
[/code]


CC BY-SA 4.0
2022-02-12T16:04:22.663
86291
2022-02-13T01:13:23.693
26573
question
c#
C# の Windows Forms アプリケーションにおいて await 前後でスレッドIDを固定したい


はい、 Main メソッド内で await で async
メソッドを待つという考え自体が間違っています。このアプローチでは、`HeavyMethodAsync`が完了してからしか、Mainメソッドの後続行は実行されません。本当に実現したいのは、

  * 重い処理（`HeavyMethodAsync`）を起動時に開始し
  * 並行してGUI（`Form1`）の表示も開始したい

ではありませんか？  
そうであれば行うべきは

  * `HeavyMethodAsync`を別スレッドで実行し
  * メインスレッドはGUI（`Form1`）の表示も開始する

必要があります。


CC BY-SA 4.0
2022-02-12T20:39:08.497
86294
2022-02-12T20:39:08.497
4236
86291
answer


質問者さんのコードで確認したわけではないですが、Main メソッドと Form1_Load メソッドでは使われる
SynchronizationContext が違うのが期待（await 前後でスレッドIDを変化させない）と違った理由ではないかと思います。

アプリによりどのように違うかは以下の記事の 5, 6, 8 を見てください。

SynchronizationContext とは？  
<http://surferonwww.info/BlogEngine/post/2020/09/30/what-is-
synchronizationcontext.aspx>

SynchronizationContext.Current プロパティで確認できるので自分のコードで確認してみてください。

たぶん、Main メソッドの方は上の記事で言うコンソールアプリと同じ、Form1_Load メソッドは Application.Run メソッド
(現在のスレッドで標準のアプリケーション メッセージ ループの実行を開始し、指定したフォームを表示) で起動される Windows Forms
ということで、 SynchronizationContext が違うと思われます。

**【追記】**

ちなみに、Main メソッドで HeavyMethodAsync("Main").Wait(); とした部分ですが、Windows Forms のような
GUI アプリでそのようなことをするとデッドロックになるはずです。詳しくは以下の記事の「すべて非同期にする」のセクションを見てください。

非同期プログラミングのベスト プラクティス  
<https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2013/march/async-
await-best-practices-in-asynchronous-programming>

そうならないようですので、そこはコンソールアプリと同じになっていると思います。


CC BY-SA 4.0
2022-02-13T01:04:26.293
86296
2022-02-13T01:13:23.693
2022-02-13T01:13:23.693
43387
43387
86291
answer