Ich habe ein Xamarin.Android-Projekt, TargetFramework = Android-Version: 8.0 (Oreo).Xamarin Android - Task.Run vs Task.Factory.StartNew und Thread.CurrentPrincipal
Ich habe folgendes Problem bemerkt:
- Set
Thread.CurrentPrincipal
zu einem benutzerdefinierten Prinzipal (besonders vorsichtig sein, um sicherzustellen, dass es serializable ist!) - Anruf
await Task.Run()
einige Aufgaben auszuführen. - Innerhalb der
Task
, wenn es auf einem anderen Thread ausgeführt wird, ist der Thread.CurrentPrincipal nicht festgelegt. - Wenn Sie Task.Factory.StartNew() anstelle von Task.Run() verwenden, wird Thread.CurrentPrincipal für den Task, der im Hintergrundthread ausgeführt wird, korrekt festgelegt.
Mit anderen Worten, Task.Factory.StartNew die CurrentPrinciple auf neue Themen zu fließen scheint, wo, wie Task.Run nicht
Außerdem, wenn Sie diesen Test wiederholen Sie auf NET 4.7, Thread.CurrentPrincipal fließt in beiden Szenarien richtig, so dass dieser Unterschied im Verhalten nur bei Mono/Xamarin.Android erscheint.
Hier ist ein Testfall:
[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);
});
}
Hier sind einige Screenshots, während das Debuggen unter Xamarin.Android Anwendung, die folgenden Dinge an verschiedenen Punkten zeigt:
- Die Thread.CurrentPrinicpal
- Der Thread.CurrentThread.ManagedThreadId
- TaskScheduler.Default
- TaskScheduler.Current
- TaskScheduler.FromCurrentSynchronizationContext();
Dies ist, was sieht es wie vor StartNew():
Hier ist, was im Inneren sieht es wie StartNew()
:
Beachten Sie, dass die Aufgabe Geplant über StartNew() wird in einem Thread-Pool-Thread ausgeführt, und der Task-Scheduler ShcnronisationContext ist null, da es kein Cu gibt rrent Synchronization Context an dieser Stelle. Beachten Sie jedoch, dass der Prinzipal des Threads korrekt auf "Daz" Identität festgelegt ist. Hier
ist, was die Dinge aussehen, nach Task.Factory.StartNew wartet() und vor dem Aufruf Task.Run():
Bitte beachte, dass wir wieder zurück auf den Hauptthread sind, nur wie wir vor dem Aufruf von StartNew() waren.
Es scheint jedoch der SyncContext TaskScheduler hat sich geändert (ID ist jetzt 3).Nicht sicher, ob das für dieses Problem relevant ist.
Jetzt ist es das, was die Dinge aussehen während Task.Run():
Hinweis: Die Thread.CurrentPrincipal.IdentityName verloren wurde (dh, weil die Prinicpal nicht auf diesen Thread geflossen als mit StartNew().
Wir sind immer noch auf einem Hintergrundthread, wie wir mit StartNew() waren. Es gibt keinen SynchronizationContext auf diesem Thread, was wir mit einem Hintergrundthread erwarten, und war der Gleiches innerhalb von StartNew() also kein Unterschied dort. Die Taskplaner von Default und Current sehen gleich aus (beide ID = 1), also auch dort kein Unterschied.
Der einzige Unterschied, den ich sehen kann, ist, dass der Auftraggeber nicht zum Thread geflossen ist.
Was ist der Grund? Ist das ein Fehler? Ich dachte Thread.CurrentPrinicpal sollte immer mit dem ExecutionContext fließen, wenn Sie Task.Run() verwenden.
Ich habe warf auch ein Problem auf Github hier: https://github.com/xamarin/xamarin-android/issues/1130
UPDATE: https://github.com/mono/mono/pull/6326/files
In Ihrem 'StartNew' befinden Sie sich immer noch auf demselben' SyncContext' wie zuvor, d. H. Dem Anfragethread. Sofern nicht anders angegeben, wird 'StartNew' auf dem aktuellen' SycnContext' ausgeführt. 'Task.Run' läuft jedoch auf einem Standardkontext, d. H. Hintergrund-Thread, und Sie sehen, dass Ihr Prinzip nicht mit gebracht wird. – JSteward
@JSteward - in meinem 'StartNew' läuft es auf einem Threadpool-Thread, und es gibt keinen SynchronizationContext. Das CurrentPrincipal des Threads ist korrekt festgelegt, so dass es aus dem aufrufenden Thread geflossen ist. Innerhalb von Task.Run() kann ich jedoch sehen, dass es auch auf einem Hintergrundthread ausgeführt wird, jedoch wurde das Thread.CurrentPrincipal nicht festgelegt. Das ist mein Problem, da unter .NET4.7 genau das Gleiche passiert, außer dass das Thread.CurrentPrincipal in beiden Szenarien richtig eingestellt ist. Also warum ist es anders unter Mono (Xamarin, Android) ist meine eigentliche Frage. – Darrell
@JSteward Ich habe Screenshots hinzugefügt, die detaillierter zeigen, wie der Thread/Scheduler/Sync-Kontext an den verschiedenen Punkten aussieht. – Darrell