74660


以下にサンプルプログラムがあります  
<https://github.com/Nao05215/WpfTest.git>

Worker.cs内にBeginメソッドが2種類あります。  
Begin1()はMessageの更新Taskに同期デリゲートを渡してその中でThread.Sleepを使って100ms毎に更新します。

[code]

    _Task = Task.Run(() =>
                {
                    int i = 0;
                    while (true)
                    {
                        try
                        {
                            Trace.Write($"TID : {Thread.CurrentThread.ManagedThreadId}\n");
                            Message = $"Number: {i++}";
                        }
                        catch(Exception e)
                        {
                            Trace.WriteLine(e.Message);
                        }
                        Thread.Sleep(100);
                    }
                });
    
[/code]

Begin2()は更新Taskに非同期デリゲートを渡してTask.Delayを使用して100ms毎に更新します。

[code]

                _Task = Task.Run(async () =>
                {
                    int i = 0;
                    while (true)
                    {
                        try
                        {
                            Trace.Write($"TID : {Thread.CurrentThread.ManagedThreadId}\n");
                            Message = $"Number: {i++}";
                        }
                        catch (Exception e)
                        {
                            Trace.WriteLine(e.Message);
                        }
                        await Task.Delay(100);
                    }
                });
    
[/code]

この時、Begin1()ではメッセージの更新は常に同じスレッド上で行われますが  
Begin2()ではawait Task.Delay()の呼び出し後にスレッドが変更されたりされなかったりするのは何故でしょうか。

また、Beginメソッドの様に、何秒かおきに処理するという場合  
より良いアプローチはBegin1とBegin2ではどちらですか？  
(無限ループなのはサンプルの為です)


CC BY-SA 4.0
2021-03-13T18:26:34.583
74658
2021-03-14T06:15:59.497
2021-03-13T21:47:45.247
4236
38100
question
c#
Task.Runでの非同期デリゲート実行時にawaitする度スレッドが切り替わるのはなぜですか


まず[Taskとは結果`T`とそれを得るための操作](https://docs.microsoft.com/ja-
jp/dotnet/standard/async-in-depth#task-and-
taskt)であり、実行方法は規定していません。操作は短時間に完了し多数実行されることを想定して、既定ではスレッドプールを使用するようです。そのため、`ManagedThreadId`はランダムに切り替わります。

操作が長時間要することが想定される場合は[`Task.Factory.StartNew`](https://docs.microsoft.com/en-
us/dotnet/api/system.threading.tasks.taskfactory.startnew?view=net-5.0)に[`TaskCreationOptions.LongRunning`](https://docs.microsoft.com/en-
us/dotnet/api/system.threading.tasks.taskcreationoptions?view=net-5.0)を指定します。この場合、スレッドプールを使わず新規にスレッドを作成するようです。

これらは別段、仕様として決まっているわけではないので今後、他の実行手段に変更される可能性もあります。あくまで短時間に多数実行される操作を効率よく完了させる／長時間を要する操作を実行する、というオプション指定でしかありません。

繰り返しになりますが、Taskは実行方法を規定していません。ですから、`ManagedThreadId`を気にするべきではありません。

* * *

> Beginメソッドの様に、何秒かおきに処理するという場合  
>  より良いアプローチはBegin1とBegin2ではどちらですか？

質問文のアプローチはどちらも処理時間に応じで次の実行開始が遅れます。それを防ごうと処理時間を測定したり行うと処理が膨れ上がります。  
そうならないように最初からタイマーを使うべきです。タイマーであれば、スレッドスケジューラーと連動して、指定したタイミングで処理が起動されます。アプリケーションレベルで管理しないため効率的に動作します。


CC BY-SA 4.0
2021-03-13T21:46:21.877
74660
2021-03-13T21:46:21.877
4236
74658
answer


Windows Forms, WPF, ASP.NET, コンソールアプリで話が変わってくると思うのですが、何の話ですか？

自分が調べた限りの話ですが、Windows Forms, WPF, ASP.NET に使用されている SynchronizationContext
はそれぞれ実装が異なっており、順に以下の通りとなります。（Current プロパティで確認できる）

・WindowsFormsSynchronizationContext  
・DispatcherSynchronizationContext  
・AspNetSynchronizationContext (.NET 4.5 以降）

それによって、それぞれ await 前後でのスレッドがどうなるかは違ってきます。詳しくは以下の記事を見てください。

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


CC BY-SA 4.0
2021-03-14T06:15:59.497
74667
2021-03-14T06:15:59.497
43387
74658
answer