Ich würde gerne Ihre Meinung zur korrekten Architektur bei der Verwendung von Task.Run
hören. Ich habe eine verzögerte Benutzeroberfläche in unserer WPF .NET 4.5-Anwendung (mit dem Caliburn Micro Framework).
Grundsätzlich mache ich (sehr vereinfachte Code-Schnipsel):
public class PageViewModel : IHandle
{
...
public async void Handle(SomeMessage message)
{
ZeigeLadeanimation();
// Macht die Benutzeroberfläche sehr träge, aber noch nicht tot
await this.contentLoader.LoadContentAsync();
VersteckeLadeanimation();
}
}
public class ContentLoader
{
public async Task LoadContentAsync()
{
await DoCpuBoundWorkAsync();
await DoIoBoundWorkAsync();
await DoCpuBoundWorkAsync();
// Ich bin nicht wirklich sicher, was alles als CPU-gebunden angesehen werden kann, was die Benutzeroberfläche verlangsamt
await DoSomeOtherWorkAsync();
}
}
Von den Artikeln/Videos, die ich gelesen/gesehen habe, weiß ich, dass await
async
nicht unbedingt auf einem Hintergrundthread ausgeführt wird und um die Arbeit im Hintergrund zu starten, muss sie mit Task.Run(async () => ... )
umschlossen werden. Die Verwendung von async
await
blockiert die Benutzeroberfläche nicht, aber sie wird immer noch auf dem UI-Thread ausgeführt, was sie träge macht.
Wo ist der beste Ort, um Task.Run zu platzieren?
Sollte ich einfach
-
den äußeren Aufruf umschließen, da dies für .NET weniger Threading-Arbeit ist?
-
, oder sollte ich nur CPU-gebundene Methoden intern mit
Task.Run
ausführen, da dies es für andere Stellen wiederverwendbar macht? Ich bin mir nicht sicher, ob es eine gute Idee ist, Arbeit auf Hintergrundthreads tief im Kern zu starten.
Ad (1), die erste Lösung wäre folgendermaßen:
public async void Handle(SomeMessage message)
{
ZeigeLadeanimation();
await Task.Run(async () => await this.contentLoader.LoadContentAsync());
VersteckeLadeanimation();
}
// Andere Methoden verwenden kein Task.Run, da jetzt alles unabhängig
// davon, ob es E/A-gebunden oder CPU-gebunden ist, im Hintergrund ausgeführt wird.
Ad (2), die zweite Lösung wäre folgendermaßen:
public async Task DoCpuBoundWorkAsync()
{
await Task.Run(() => {
// Führe hier viel Arbeit aus
});
}
public async Task DoSomeOtherWorkAsync(
{
// Ich bin mir nicht sicher, wie ich diese Methoden behandeln soll -
// wahrscheinlich muss ich eine nach der anderen testen, ob sie die Benutzeroberfläche verlangsamen
}