【Windows 8開発】非同期プログラミング進級編の単一スレッドスイートルーム(STA)およびtask実行コンテキストの制御方法


(注意してください.本稿の概念はかなり重要で、開発においてかなり役に立ちます)
まずコードを見てみましょう
void SampleCpp::MainPage::Btn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
     auto workItemDelegate = [this]() {
          // do something here
     };
     create_task(workItemDelegate).then([this]{
          this->Btn->Content = "New";
     });
}

本来は、workItemDelegateのいくつかの処理を実行した後、UIのボタン名を更新したが、例外が投げ出され、ボタン名が更新されていないことに気づいた.次のように少し修正して、結果を見てみましょう.
void SampleCpp::MainPage::Btn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
     auto op = create_async([this]() {
          // do something here
     });
     create_task(op).then([this]() {
         this->Btn->Content = "New";
     });
}

今度はすべて正常になった.どうしてですか.この問題を持って本文に入りましょう.
まず一つの概念を理解する:STA(single-threaded apartment)は、ひとまず単一スレッドスイートルームに翻訳され、Comスレッドモデルを熟知している可能性がある.説明しにくいので、ここではできるだけ簡単に説明します.例えば、モジュールには、外部がこれらの機能をどのように呼び出しても、外部がどれだけのスレッドでこれらの機能を呼び出しても、これらの機能がいつまでも同じスレッドでしか実行されないことを保証する機能があります.STAモジュールはメッセージキューを管理し、メッセージを受信するとメッセージに指定された機能の呼び出しを実行し、実行結果を呼び出し元に返す.外部の異なるスレッドでこれらの機能を呼び出すのは、モジュールに一連の呼び出しメッセージを送信し、モジュールメッセージキューで実行を管理するため、同じスレッドで実行されることを保証することができます.理解できないのはgoogleの下でsingle-threaded apartmentという概念で、詳しい文章は多いはずです.
STAに言及する理由は、MetroプログラムにおいてUIは単一スレッドスイートルーム(STA)で実行されるため、UI更新に関するすべての操作はUIスレッドで実行されなければならないからである.以前の最初のセグメントコードが異常になったのは、thenでのUIの更新処理がbackgroundスレッドで実行されるためであり、UI単一スレッドスイートルームの原則に反する.では、2段目はなぜ正常な結果になったのでしょうか.
なぜならWinRTには、taskのLambda関数の戻りタイプがIAsyncActionまたはIAsyncOperationである場合、このtaskが単一スレッドスイートルームで作成されると、デフォルトでは、そのすべての後続タスク(thenでの処理)がこの単一スレッドスイートルームで実行されるという定義があるからである.上記の2番目のセグメントコードのcreate_asyncの戻り値はIAsyncActionであるため、thenでのUI更新処理はUIスレッドで実行され、正常に実行される.その後も複数のthen処理が連続していても、すべてのtaskは同じです. continuationは、taskの単一スレッドスイート(この例ではUIスレッドスイート)で実行されるので、以下のコードはあなたが考えているように、走ることができます.
void SampleCpp::MainPage::Btn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
     auto op = create_async([this]() {
          // do something here
     });
     create_task(op).then([this]() {
         this->Btn->Content = "New1";
     }).then([this]() {
         this->Btn->Content = "New2";
     });
}

ここで必ず、taskのLambda戻り値によってtask実行コンテキストを決定する以外に、コードの中で自分で決定することができますか?答えはもちろん肯定的だ.WinRTはtask_を提供していますcontinuation_contextでは、次の3つの静的メソッドを使用できます.
task_continuation_context::use_current()     現在のコンテキストでtaskを実行する
task_continuation_context::use_arbitrary()   backgroundスレッドでtaskを実行する
task_continuation_context::use_default()     デフォルトでtaskを実行する
上記の3つのメソッドの戻り値をthen関数の2番目のパラメータとして渡すことができます(関数のデフォルトパラメータはuse_defaultを伝えるのと同じです)、コードを見るとすべてがわかります.最初のエラーのコードを以下のように変更します.use_Currentはthen関数の処理もUIスレッドで実行し、実行結果は正常です.
void SampleCpp::MainPage::Btn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
     auto workItemDelegate = [this]() {
          // do something here
     };
     auto context = task_continuation_context::use_current();
     create_task(workItemDelegate).then([this]{
          this->Btn->Content = "New";
     }, context);
}

では、どの実行コンテキストを使用するかをどのように決定しますか?これは私が言わなくてもわかるでしょう.一般的に、論理アルゴリズムの実行にはuse_が使用できます.Arbitraryはbackgroundスレッドで実行させ、UIの応答に影響を与えない.UIに関する処理はuse_CurrentはUIスレッドで実行するように指定します.
ここまで言うと、このような処理方法に優位性があるとは思わないか、あまり役に立たないとは思わないかもしれませんが、マルチスレッド開発、スレッドモデルなどの面ではまだ幼いかもしれません.UIに要約されたスレッドモデルを構築するのが一般的であることを知っておくには、バックグラウンドスレッドが実行された後にUIスレッドを戻す方法を考える必要は避けられません.従来のthread APIで類似の特性を実現し、APIの使いやすさと安定性を両立させるには、容易な仕事ではないことを知っておく必要があります.だからwinRTのtaskを使ってとても便利ないくつかの机能を実现することができることを発见します时、私は一喜一忧で、喜んでWinRTのtaskを使ってパラレルタスクの开発を行うのは本当に便利で、効率が高すぎて、MSは本当に盖ではありませんて、困っているのは、WinRTはパラレル开発に対して、非同期プログラミング、マルチスレッドの开発はAPIが大きく颜を変えましたこれは、プラットフォームをまたぐモジュールの開発に多くの難易度を増加させた.
最後に、冒頭のコードの問題など、WinRTのThreadPoolを使用しても同様の問題が発生し、次のようなプログラムでもUIを正常に更新できません.
auto workItemDelegate = [this](IAsyncAction^ workItem) {
       this->Btn->Content = "thread";
};
auto workItemHandler = ref new Windows::System::Threading::WorkItemHandler(workItemDelegate);
Windows::System::Threading::ThreadPool::RunAsync(workItemHandler, Windows::System::Threading::WorkItemPriority::Low);

Windows::UI::Core::CoreDispatcherのDispatcherでこの問題を解決することができますが、taskの解決方法に比べて、面倒なことが多くなりました.これも私が非同期プログラミング進級編シリーズの冒頭の文章でWinRTのThreadPoolをできるだけ使わないと言った理由です.taskがもっと強く、もっと便利だからです.
後続はtaskのいくつかの特性を紹介して、異なる観点の歓迎の伝言があります!