.NET 4.0 以降、非同期タスクを実行するための TPL があります。msdn を読んでいる場合、フォーム/UI と対話するすべての非同期操作は、引き続き InvokeRequire ... Invoke() パターンを使用します。私が尋ねているのは、それには理由がありますか?私が学んだことから、TPL は古いスレッド化メカニズムの一種に取って代わるべきです。では、UI のスレッド化に関してそれを無視するポイントは何でしょうか? それについて何か考えはありますか?
4 に答える
これはかなり主観的なようです...
「Since .NET 4.0」と言うとき、あなたは「今年の 4 月の時点で」と言っています。.net は現在 10 年間使用されており、InvokeRequired/Invoke は過去 9 年間使用されています。なんらかの理由でUIコード?スレッドを呼び出す新しい方法が存在したとしても、大きな互換性の問題なしにパターンを単純に変更することはできませんでした。
また、TPL は InvokeRequired/Invoke とは異なります。TPL は簡単な並列処理に関するものであり、invoke は特定のスレッドでコードを実行することに関するものです。互換性の問題がなかったとしても、なぜ一方が他方を置き換えるのかわかりません。
正しいスレッドで UI コンポーネントを確実に呼び出すために、TPL を使用することを妨げるものは何もないことに注意してください。実際、これは簡単に行うことができます。しかし、それはあなた次第であり、現在の API が下位互換性を失うような形で変更されることはありません。
TPLでは、 TaskScheduler.FromCurrentSynchronizationContextを使用してターゲットスレッドを指定できます。このメソッドは、タスクがメインスレッドで実行されることを指定します。Invokeの代わりに使用することをお勧めします。
ここでの質問は何ですか?TPLが存在しても、UIが本質的にシングルスレッドであり、UIスレッドでのみコントロールにアクセスする必要があるという事実は変わりません。(これはWindowsの制限であり、.NET UIフレームワークの制限ではありません。TPLは数十年にわたるWindowsの設計上の制限を変更することはできません。)
タスクとInvokeRequired/Invokeの混合に関する質問の場合は、InvokeよりもTPL指向の方法があります。TPLには、UIスレッドで実行する継続タスクをスケジュールするための組み込みの方法が付属しています。したがって、バックグラウンド指向の作業を1つのタスクに入れてから、UIを別のタスクに更新します。タスクスケジューラとSynchronizationContextに関するこの投稿を参照してください。
(しかし、実際には、TPLは古いスレッドAPIを置き換えません。Invokeがあなたがやろうとしていることを行うための最良の方法である場合は、それを使用してください。)
private void WorkProcessingAsync(IWorkItem workItem)
{
IsBusy = true;
/* =============================
* Create a TPL Task and pass the current UiCulture in an state Object to resolve the correct .resx file for translation / globalisation / Multilanguate features in Background Thread
* ==============================*/
Task<IWorkItem> task = Task.Factory.StartNew((stateObj) =>
{
// here we are already in the task background thread
// save cast the given stateObj
var tuple = stateObj as Tuple<IWorkItem, CultureInfo>;
Debug.Assert(tuple != null, "tuple != null");
Thread.CurrentThread.CurrentUICulture = tuple.Item2; // Here we set the UI-Thread Culture to the Background Thread
var longRunningOperationAnswer = LongRunningOperation.DoLongWork(tuple.Item1);
return longRunningOperationAnswer;
}, new Tuple<IWorkItem, CultureInfo>(workItem, Thread.CurrentThread.CurrentUICulture)); // here we pass the UI-Thread Culture to the State Object
/* =======================================================================
* Handle OnlyOnRanToCompletion Task and process longRunningOperationAnswer back in UiThread
* =======================================================================*/
task.ContinueWith((t) =>
{
IsBusy = false;
// handle longRunningOperationAnswer here in t.Result
Log.Debug("Operation completet with {0}", t.Result);
}, CancellationToken.None
, TaskContinuationOptions.OnlyOnRanToCompletion
, TaskScheduler.FromCurrentSynchronizationContext());
/* =======================================================================
* Handle OnlyOnFaulted Task back in UiThread
* =======================================================================*/
task.ContinueWith((t) =>
{
IsBusy = false;
AggregateException aggEx = t.Exception;
if (aggEx != null)
{
aggEx.Flatten();
Log.ErrorFormat("The Task exited with Exception(s) \n{0}", aggEx);
foreach (Exception ex in aggEx.InnerExceptions)
{
if (ex is SpecialExaption)
{
//Handle Ex here
return;
}
if (ex is CustomExeption)
{
//Handle Ex here
return;
}
}
}
}, CancellationToken.None
, TaskContinuationOptions.OnlyOnFaulted
, TaskScheduler.FromCurrentSynchronizationContext());
}