2017-12-21 56 views
2

私はXamarin.Androidプロジェクト、TargetFramework = Androidバージョン:8.0(Oreo)を持っています。Xamarin Android - Task.RunとTask.Factory.StartNewとThread.CurrentPrincipal

私は、次のような問題に気づいている:カスタムプリンシパル(!それを確実にするために、余分な慎重であることは直列化可能である)

  • いくつかのタスクを実行するためのコールawait Task.Run()に設定Thread.CurrentPrincipal

    1. を。
    2. Taskの中で、別のスレッドで実行すると、Thread.CurrentPrincipalは設定されません。
    3. Task.Run()の代わりにTask.Factory.StartNew()を使用すると、バックグラウンドスレッドで実行されるTaskでThread.CurrentPrincipalが正しく設定されます。言い換えれば

    、Task.Factory.StartNewあなたはNET 4.7上でこのテストを繰り返した場合Task.Runはさらに、のThread.CurrentPrincipalが流れていない

    ない場所として、新しいスレッドにCurrentPrincipleを流れているようですどちらのシナリオでも正しく動作するため、この動作の違いはMono/Xamarin.Androidでのみ実行されます。ここで

    は、テストケースである:

    1. Thread.CurrentPrinicpal
    2. :ここ

      [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

    3. TaskScheduler.Default
    4. TaskScheduler.Current
    5. TaskScheduler.FromCurrentSynchronizationContext();

    これは)物事がStartNew(前にどのように見えるかです:ここでは

    enter image description here

    は物事がStartNew()内でどのように見えるかです:

    enter image description here

    注タスク、ということStartNew()経由でスケジュールされたスレッドプールスレッドで実行され、cuが存在しないためShcnronisationContext TaskSchedulerはnullです現時点では同期コンテキストです。ただし、スレッドのプリンシパルが "Daz" IDに正しく設定されていることにも注意してください。ここで

    物事が(Task.Factory.StartNewを待った後、どのように見えるかである)とTask.Runを(呼び出す前):ちょうど私たちは再びメインスレッドにある

    enter image description here

    注意、 StartNew()を呼び出す前のようでした。

    ただし、SyncContext TaskSchedulerが変更されたようです(IDは3になりました)。それがこの問題に関連しているかどうかは不明です。

    今これは)物事がTask.Run(中にどのように見えるかです:

    enter image description here

    注: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

  • +0

    あなたの 'StartNew'ではあなたは依然として前と同じ同じ' SyncContext'、つまりリクエストスレッドにいます。特に指定がない限り、 'StartNew'は現在の' SycnContext'で実行されます。 'Task.Run'はデフォルトのコンテキスト、すなわちバックグラウンドスレッド上で動作しますが、あなたの原則が持ち込まれていないのが分かります。 – JSteward

    +0

    @ JSteward - 私の 'StartNew'にはスレッドプールスレッドで実行されていて、SynchronizationContextはありません。スレッドのCurrentPrincipalが正しく設定されているため、呼び出し元のスレッドからフローされました。しかし、Task.Run()内では、バックグラウンドスレッドでも実行されていますが、Thread.CurrentPrincipalは設定されていません。これは私の問題です。これは.NET4.7ではまったく同じですが、Thread.CurrentPrincipalが両方のシナリオで正しく設定されている点を除いて同じことが起こります。それで、なぜMono(Xamarin、Android)の下でそれが違うのが私の本当の質問です。 – Darrell

    +0

    @JSteward私はさまざまな点でスレッド/スケジューラ/同期のコンテキストがどのように見えるかの詳細を示すスクリーンショットを追加しました。 – Darrell

    答えて

    0

    を編集作業を開始:

    私はこの回答を残しておきますが、それは質問には関係がありませんので、私はそれを黙っていませんが、それをマークしないでください。関連性があるかもしれないその下の通信があります。

    私は質問に近づき、何が尋ねられているのかを理解し、正直に私がこれがバグかもしれないと感じることに同意することができます。私はそれを説明することができず、これが前進することを知っていることが良いと感じている。私は自分自身でこの問題に目を留めていくつもりです。

    編集終了:

    私は私はあなたがやっているのか理解全くわかりません。ブレークポイントを使用して、変数上にカーソルを移動することはできますか?これらの変数を2つのタスクの外に置き、実行した後にそれらの変数を表示します。

    しかし、私があなたが探している答えはここにあります。 Fooを入力すると、Task戻り型の非同期メソッドに過ぎません。はい、これは呼び出されると待つことができますが、実際に新しいスレッドで実行されるまで待つことはありません。したがって、Fooがawait Task.Factory.StartNewに達するまで、文脈はawait Foo()となります。

    Task.Factory.StartNewを呼び出すと、スレッドプールからスレッドを取得し、そのスレッドで渡されたアクションを実行します。完了すると、呼び出されたのと同じコンテキストでFooに戻る予定です。今度は、文脈がawait Task.Runの場合、await Task.Factory.StartNewとまったく同じことを繰り返します。

    2つの呼び出しに1つの違いがあり、​​パラメータがTaskFactoryに渡されています...正直なところどの工場ですか?それは具体的ではなく、すべての環境で同じではありませんが、あなたのスレッドがより長い期間生きていることを工場に知らせるためにそこにあります。私はそれが長いスレッドを実行するために意図されているスレッドの別のプールからスレッドを引っ張ると思うが、私は完全にはわからない。どちらにしても;同じ呼び出しではなく、呼び出しごとに新しいスレッドがあり、その間に同じスレッドに戻ります。

    また、awaitコールの最後に.ConfigureAwait(false);を使用することもできますが、これは呼び出し元のコンテキストには戻りません。それはあなたが気づいていない場合に問題を引き起こすかもしれない時間に最適なものでスレッドを実行し続けるようにスケジュールします。全体的にはより効率的ですが、残りのメソッドが同じコンテキストで操作する必要がないことを確認してください。

    これが役に立ちます。

    +1

    ありがとう、私はより明確に説明しようと私の質問を更新します。私がやっていることは、現在のスレッドのプリンシパルを設定し、Task.Run()を呼び出すときにプリンシパルがタスクが実行するスレッドに流れないことです(それが現在のスレッドで実行されるようにスケジュールされていない限り) - ただし、Task.Factory.StartNew()を呼び出すと、prinipalは新しいスレッドに流れます。この動作は、Xamarin Android(モノラル)でのみ発生します。完全なNETフレームワーク4.7では、プリンシパルはどちらの場合も正しく流れます。これがバグかどうか知りたいです。 – Darrell

    +0

    デバッグ中にさまざまな点のスクリーンショットが表示され、うまくいけばもっと深い説明が追加されました。 – Darrell

    +0

    @Darrell Hey Darrell、私は私の答えを更新しました。これは答えではありません。質問を更新していただきありがとうございます。私はあなたが今言っていることを正確に見て、これを自分では気づかなかった。私も少し掘り出すことにしました。これはバグだと思います。非常に奇妙な...私は答えをマークアップし、私は自分自身の答えにそれをフォローしたいので、お気に入りとして配置しました。 –

    関連する問題