Auf den Methoden Task.Factory.StartNew und Task.Run

Wenn Sie Aufgaben mit den Methoden Task.Factory.StartNew oder Task.Run erstellen, sollten Sie beim Schreiben von asynchronem Code bestimmte wichtige Punkte berücksichtigen. In den meisten Fällen ist es ratsam, die Verwendung der Task.Factory.StartNew-Methode zu vermeiden, wenn Sie mit asynchronem Code arbeiten. Wenn Sie mit parallelem Code arbeiten, würde ich sagen, dass StartNew eine gute Wahl ist.

Ein Taskplaner ist eine Komponente, die für die Planung von Aufgaben verantwortlich ist. Das .NET-Framework bietet Ihnen zwei Taskplaner. Es gibt den Standard-Taskplaner, der im Thread-Pool des .NET-Frameworks ausgeführt wird, und den Taskplaner, der im Synchronisationskontext eines angegebenen Ziels ausgeführt wird. Der Standard-Taskplaner reicht die meiste Zeit aus. Sie können jedoch auch einen eigenen benutzerdefinierten Taskplaner erstellen, um zusätzliche Funktionen bereitzustellen. Um Ihren eigenen benutzerdefinierten Taskplaner zu erstellen, müssen Sie eine Klasse erstellen, die die System.Threading.Tasks.TaskScheduler-Klasse erweitert.

Wie erstelle ich Aufgaben mit der Task Parallel Library?

Es gibt verschiedene Möglichkeiten, wie Sie Aufgaben in .Net erstellen und starten können. Sie müssen die Klasse System.Threading.Tasks.Task oder System.Threading.Tasks.Task verwenden, um Aufgaben zu erstellen (eine planbare Arbeitseinheit). Während Ersteres zum Erstellen einer Aufgabe verwendet wird, die keinen Wert zurückgibt, wird Letzteres zum Erstellen von Aufgaben mit Rückgabewerten verwendet. Die Task.Factory-Eigenschaft ist eine Instanz der TaskFactory-Klasse. Diese Eigenschaft wird zum Erstellen und Planen von Aufgaben verwendet. Während die Task.Factory.StartNew-Methode wie eine Fork-Operation funktioniert und zum Erstellen und Starten neuer Tasks verwendet wird, funktioniert die Wait-Methode wie eine Join-Operation und wartet auf den Abschluss der Task.

Das folgende Codeausschnitt zeigt, wie Sie die Task.Factory.StartNew-Methode verwenden können.

Task.Factory.StartNew(() => TestMethod(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);

Sie können eine Aufgabe auch mit der Task.Run-Methode erstellen, wie im folgenden Codeausschnitt gezeigt.

public async Task DoSomeWork()

        {

            await Task.Run(() => TestMethod());

        }

        void TestMethod()

        {

            Console.WriteLine("Hello world!");

        }

Wenn Sie einen Wert von einer Aufgabe zurückgeben möchten, können Sie die Task.FromResult-Methode nutzen, wie im folgenden Codeausschnitt gezeigt.

public async Task DoSomeWork()

   {

      string text = await Task.FromResult(GetMessage());

   }

private string GetMessage()

   {

      return "Hello world!";

   }

Sie können Aufgaben auch mit einem Delegaten oder einer Aktion erstellen. Das folgende Codeausschnitt zeigt, wie Sie mithilfe von Aktionen und Delegaten Aufgaben erstellen können.

Task task1 = new Task (new Action(Display));

task1.Start();

Task task2 = new Task (delegate { Display(); });

task2.Start();

Sie können Aufgaben auch mit Lamba und anonymen Methoden erstellen.

Task.Factory.StartNew und Task.Run

Task.Factory.StartNew ist eine schnelle Methode zum Erstellen und Starten einer Task. Beachten Sie, dass ein Aufruf von Task.Factory.StartNew funktional dem Erstellen einer Taskinstanz und dem anschließenden Aufrufen der Start-Methode für die Instanz entspricht. Es wird jedoch aus zahlreichen Gründen nicht empfohlen, es zu verwenden. Wenn Sie synchronen Code ausführen möchten, ist Task.Factory.StartNew keine gute Wahl.

Beachten Sie, dass die StartNew-Methode die Aufgabe auf diesem Taskplaner ausführt, wenn ein Taskplaner verfügbar ist. Im Gegenteil, wenn ein Scheduler nicht verfügbar ist, würde er die Aufgabe in einem Thread-Pool-Thread ausführen. Es ist zu beachten, dass Task.Factory.StartNew standardmäßig TaskScheduler.Current und nicht TaskScheduler.Default ist.

Beachten Sie, dass ein Aufruf von Task.Run (Aktion) der folgenden Anweisung entspricht: Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

Im Gegenteil, ein Aufruf von Task.Factory.StartNew (Aktion) entspricht der folgenden Anweisung:

Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current);

Wenn Sie Task.Factory.StartNew verwenden möchten, wenn Sie einen benutzerdefinierten Taskplaner erstellt und die Scheduler-Instanz explizit an ihn übergeben haben. Ich würde immer empfehlen, Task.Run zu verwenden, da es viel einfacher ist und sicherere Standardeinstellungen hat. Mit anderen Worten, wir sollten die Verwendung von Task.Factory.StartNew vermeiden, es sei denn, Sie müssen einen Taskplaner erstellen und ihn dann explizit übergeben, wenn Sie die StartNew-Methode aufrufen, um eine neue Task zu erstellen und zu planen. Wenn Sie die TaskFactory.StartNew-Methode effektiv und zuverlässig verwenden möchten, sollten Sie einen benutzerdefinierten Taskplaner verwenden und dann die Optionen CancellationToken und TaskCreationOptions angeben.

Die Task.Run-Methode wird empfohlen, wenn Sie die Thread-Planung und ihre Feinheiten nicht genau steuern müssen. Sie sollten Task.Run hauptsächlich für CPU-gebundene Methoden verwenden. Sie sollten jedoch Task.Run verwenden, während Sie die Aufgabe aufrufen, und nicht innerhalb der Implementierung der Aufgabe. Mit anderen Worten, Sie sollten Task.Run nicht in einer Implementierung einer Methode verwenden, sondern an dem Punkt, an dem die Methode aufgerufen wird. Das folgende Codefragment ist beispielsweise ein Beispiel für einen "schlechten" Code.

public async Task DownloadDataFromWebAsync(Uri uri)

        {

            return await Task.Run(() =>

            {

                using (WebClient webClient = new WebClient())

                {

                    return webClient.DownloadString(uri);

                }

            });

        }

Siehe das oben angegebene Code-Snippet. Die Methode ist nicht skalierbar, da sie den Hintergrundthread blockieren, einen Thread aus dem Threadpool abrufen und synchron ausführen würde. Daher würde es mehr Ressourcen in Ihrem System verbrauchen.