2013-04-25 9 views
7

で実行されている理由:非同期コールバックは、コードのこの部分についてはWPFのUIスレッド

は、次の出力
static async Task<string> testc() 
{ 
    Console.WriteLine("helo async " + Thread.CurrentThread.ManagedThreadId); 
    await Task.Run(() => { 
     Thread.Sleep(1000); 
     Console.WriteLine("task " + Thread.CurrentThread.ManagedThreadId); 
    }); 
    Console.WriteLine("callback "+Thread.CurrentThread.ManagedThreadId); 
    return "bob"; 
} 

static void Main(string[] args) 
{ 
    Console.WriteLine("helo sync " + Thread.CurrentThread.ManagedThreadId); 
    testc(); 
    Console.WriteLine("over" + Thread.CurrentThread.ManagedThreadId); 
    Thread.Sleep(2000); 
    Console.ReadLine(); 
} 

私が手:

helo sync 10 
helo async 10 
over10 
task 11 
callback **11** 

OKです:のawait後のコードの一部がで実行されますタスク自体と同じスレッド。今

、私はWPFアプリケーションでそれを行う場合:私たちはUIスレッドで実行待った後、コードを見ることができ

helo sync 8 
helo async 8 
over8 
task 9 
callback **8** 

private void Button_Click_1(object sender, RoutedEventArgs e) 
{ 
    Console.WriteLine("helo sync " + Thread.CurrentThread.ManagedThreadId); 
    testc(); 
    Console.WriteLine("over" + Thread.CurrentThread.ManagedThreadId); 
    Thread.Sleep(2000); 
    Console.ReadLine(); 
} 

それが出力を次生成します。それは観測可能なコレクションなどを操作することができるので、これは素晴らしいことです。しかし、私は "なぜ?" "どうすれば同じことができますか?"これはTaskSchedulerの動作に関連していますか?これは.NET Frameworkでハードコードされていますか?

Thxあなたが提出できるアイデアは、

答えて

7

理由は、UIスレッドからタスクを開始するときにTask.RunがWPFアプリケーションに存在する場合は、SynchronizationContextが存在する場合にキャプチャするためです。タスクはSynchronizationContextを使用して、UIスレッドへのコールバックをシリアル化します。ただし、コンソールアプリケーションのようにコンテキストが使用可能でない場合、コールバックは別のスレッドで発生します。

スティーブントブは、これをblog entryに記載しています。

BTW タスクでThread.Sleepを使用しないでください。タスクが1つのスレッドに束縛されない可能性があるため、異常な動作が発生する可能性があります。代わりにTask.Delayを使用してください。

+0

+1あなたのBTWに重点を置く必要があります。この完全な答えは – Phill

2

しかし、私は「なぜ」と思っていましたか?

まあ、これはそれが観測コレクションなどを操作可能にするので...非同期の

全体のポイントは、非同期性を容易にすることです素晴らしいです:あなたは自分自身と答えました

実際には非同期の "同期的な"コードを書くことができます。これは、非同期メソッド全体(UIスレッドをブロックせずに)を一時的に「一時停止」するために、何かを待つ必要があるときに、1つのコンテキスト(たとえばUIスレッド)内に固執することを含む場合があります。

「どうすれば同じことができますか?」

ここで何を意味するのかはっきりしません。基本的に、Taskの待ち受け可能なパターンの実装では、ConfigureAwait(false)を明示的にオプトアウトしない限り、どのスケジューラがコールバックを送信するかを決定するためにTaskScheduler.FromCurrentSynchronizationContext()を使用します。それはそれを管理する方法です...あなたが "同じことをする"ことができるかどうかは、あなたが何をしようとしているかによって決まります。

待望のパターンの詳細については、async/await FAQの「待つもの」の質問を参照してください。

+0

「どうすれば同じことができますか?」彼は、「WPFを使わずにWPFの動作をどうやって得るのですか? –

+0

@ jdv-JandeVaan:それが意味だったら、それは非常に不明です。とにかく、私が与えた細部はOPをさらに進めるための十分な情報を提供することを願っています。彼らは、必要に応じていつでも詳細を尋ねることができます。 –

+0

Ok .. Fine。私は本当に同じことをしたくありません。私は、コールバックがDispatcher.Invokeメソッドにどのように送られているのだろうと思っていました...そしてもし私ができれば、最終的に、同様のことをするか、それがフレームワークでコード化されていました。あなたの答えのためのThx – Kek

0

私のasync/await introが参考になる場合があります。他の答えはほぼです。まだデフォルトA「文脈」で、完了していない

ときawaitTaskをするときTask完了方法を再開するために使用される捕獲されます。この「文脈」は であり、それがヌルでない限りであり、その場合はTaskScheduler.Currentです。

注動作するように、このために必要な条件:

  • 「あなたawait ...」 - あなたはTask.ContinueWithで、例えば、手動継続をスケジュールした場合、何のコンテキストキャプチャは行われません。あなたは(SynchronizationContext.Current == null ? TaskSchedler.Current : TaskScheduler.FromCurrentSynchronizationContext())のようなものを使ってそれを自分で行わなければなりません。 "Task ... await ..."
  • - この動作はTask種類ためawait行動の一部です。他のタイプでも同様のキャプチャが行われる場合とされない場合があります。
  • 「...それはまだ完了していません...」 - Taskはそれはawaitエド時間ですでに完了している場合は、async方法は同期を継続していきます。その場合、コンテキストをキャプチャする必要はありません。
  • "...デフォルトでは..." - これはデフォルトの動作であり、変更することができます。特にTask.ConfigureAwaitメソッドを呼び出し、continueOnCapturedContextパラメータにはfalseを渡します。このメソッドは、パラメータがfalseの場合にキャプチャされたコンテキストでは続行されない待機可能な型(Taskではなく)を返します。
+0

Thxです。そのほとんど(または私の質問に答えるのに少なくとも十分)は@Jakob Christensenの答えの中にありましたが、私はこの完全な見解を謝ります! – Kek

関連する問題