私はXamarin.Androidプロジェクト、TargetFramework = Androidバージョン:8.0(Oreo)を持っています。Xamarin Android - Task.RunとTask.Factory.StartNewとThread.CurrentPrincipal
私は、次のような問題に気づいている:カスタムプリンシパル(!それを確実にするために、余分な慎重であることは直列化可能である)
await Task.Run()
に設定Thread.CurrentPrincipal
- を。
Task
の中で、別のスレッドで実行すると、Thread.CurrentPrincipalは設定されません。- Task.Run()の代わりにTask.Factory.StartNew()を使用すると、バックグラウンドスレッドで実行されるTaskでThread.CurrentPrincipalが正しく設定されます。言い換えれば
、Task.Factory.StartNewあなたはNET 4.7上でこのテストを繰り返した場合Task.Runはさらに、のThread.CurrentPrincipalが流れていない
ない場所として、新しいスレッドにCurrentPrincipleを流れているようですどちらのシナリオでも正しく動作するため、この動作の違いはMono/Xamarin.Androidでのみ実行されます。ここで
は、テストケースである:
- Thread.CurrentPrinicpal
- :ここ
[Fact] public async Task LoginService_WhenUserLogsIn_PrincipalFlowsToAsyncTask() { var mockIdentity = new MockIdentity(true); var mockPrincipal = new MockPrincipal(mockIdentity); Thread.CurrentPrincipal = mockPrincipal ; await Task.Factory.StartNew(async() => { var newThreadId = Thread.CurrentThread.ManagedThreadId; // on different thread. Assert.True(Thread.CurrentPrincipal.Identity.IsAuthenticated); Assert.Equal(mockPrincipal, Thread.CurrentPrincipal); await Task.Factory.StartNew(() => { // still works even when nesting.. newThreadId = Thread.CurrentThread.ManagedThreadId; Assert.True(Thread.CurrentPrincipal.Identity.IsAuthenticated); Assert.Equal(mockPrincipal, Thread.CurrentPrincipal); }, TaskCreationOptions.LongRunning); }, TaskCreationOptions.LongRunning); await Task.Run(() => { // Following works on NET4.7 and fails under Xamarin.Android. var newThreadId = Thread.CurrentThread.ManagedThreadId; Assert.True(Thread.CurrentPrincipal.Identity.IsAuthenticated); Assert.Equal(mockPrincipal, Thread.CurrentPrincipal); }); }
は異なるポイントで、次のことを示す、Xamarin.Androidアプリケーションの下でデバッグしながら、いくつかのスクリーンショットですThread.CurrentThread.ManagedThreadId
- TaskScheduler.Default
- TaskScheduler.Current
- TaskScheduler.FromCurrentSynchronizationContext();
これは)物事がStartNew(前にどのように見えるかです:ここでは
は物事がStartNew()
内でどのように見えるかです:
注タスク、ということStartNew()経由でスケジュールされたスレッドプールスレッドで実行され、cuが存在しないためShcnronisationContext TaskSchedulerはnullです現時点では同期コンテキストです。ただし、スレッドのプリンシパルが "Daz" IDに正しく設定されていることにも注意してください。ここで
物事が(Task.Factory.StartNewを待った後、どのように見えるかである)とTask.Runを(呼び出す前):ちょうど私たちは再びメインスレッドにある
注意、 StartNew()を呼び出す前のようでした。
ただし、SyncContext TaskSchedulerが変更されたようです(IDは3になりました)。それがこの問題に関連しているかどうかは不明です。
今これは)物事がTask.Run(中にどのように見えるかです:
注:Thread.CurrentPrincipal.IdentityNameが失われた(すなわちPrinicpalは、このスレッドに流れていないため、これはStartNew()で行ったのと同じです。
私たちはStartNew()と同じようにバックグラウンドスレッドにいます。 このスレッドにはSynchronisationContextはありません。 StartNew()内で同じなので違いはありません デフォルトおよび現在のタスクスケジューラは同じに見えます(両方ともID = 1)ので、違いはありません。
唯一の違いは、プリンシパルがスレッドに流れていないことです。
理由は何ですか?これはバグですか? Thread.CurrentPrinicpalは、Task.Run()を使用するときに必ずExecutionContextを使用してフローする必要があります。
私はここのGithub上の問題をも提起している:https://github.com/xamarin/xamarin-android/issues/1130
UPDATE:これがうまくいけば、それはで固定され、バグとして認識されているように見えます:https://github.com/mono/mono/pull/6326/files
あなたの 'StartNew'ではあなたは依然として前と同じ同じ' SyncContext'、つまりリクエストスレッドにいます。特に指定がない限り、 'StartNew'は現在の' SycnContext'で実行されます。 'Task.Run'はデフォルトのコンテキスト、すなわちバックグラウンドスレッド上で動作しますが、あなたの原則が持ち込まれていないのが分かります。 – JSteward
@ JSteward - 私の 'StartNew'にはスレッドプールスレッドで実行されていて、SynchronizationContextはありません。スレッドのCurrentPrincipalが正しく設定されているため、呼び出し元のスレッドからフローされました。しかし、Task.Run()内では、バックグラウンドスレッドでも実行されていますが、Thread.CurrentPrincipalは設定されていません。これは私の問題です。これは.NET4.7ではまったく同じですが、Thread.CurrentPrincipalが両方のシナリオで正しく設定されている点を除いて同じことが起こります。それで、なぜMono(Xamarin、Android)の下でそれが違うのが私の本当の質問です。 – Darrell
@JSteward私はさまざまな点でスレッド/スケジューラ/同期のコンテキストがどのように見えるかの詳細を示すスクリーンショットを追加しました。 – Darrell