W metodach Task.Factory.StartNew i Task.Run

Podczas tworzenia zadań przy użyciu metod Task.Factory.StartNew lub Task.Run należy pamiętać o kilku ważnych kwestiach podczas pisania kodu asynchronicznego. W większości przypadków zaleca się unikanie metody Task.Factory.StartNew, jeśli pracujesz z kodem asynchronicznym. Jeśli pracujesz z kodem równoległym, powiedziałbym, że StartNew to dobry wybór.

Harmonogram zadań to składnik odpowiedzialny za planowanie zadań; Platforma .Net udostępnia dwa programy do planowania zadań. Istnieje domyślny harmonogram zadań, który działa w puli wątków platformy .Net, i harmonogram zadań, który jest wykonywany w kontekście synchronizacji określonego celu. Domyślny harmonogram zadań będzie wystarczający przez większość czasu, ale możesz także zbudować własny niestandardowy harmonogram zadań, aby zapewnić dodatkowe funkcje. Aby zbudować własny, niestandardowy harmonogram zadań, należałoby utworzyć klasę rozszerzającą klasę System.Threading.Tasks.TaskScheduler.

Jak tworzyć zadania przy użyciu biblioteki równoległej zadań?

Istnieje kilka sposobów tworzenia i uruchamiania zadań w .Net. Do tworzenia zadań (jednostki pracy, którą można zaplanować), należy użyć klasy System.Threading.Tasks.Task lub System.Threading.Tasks.Task. Podczas gdy pierwsza jest używana do tworzenia zadania, które nie zwraca wartości, druga jest używana do tworzenia zadań, które mają zwracane wartości. Właściwość Task.Factory jest instancją klasy TaskFactory. Ta właściwość służy do tworzenia i planowania zadań. Podczas gdy metoda Task.Factory.StartNew działa jak operacja rozwidlenia i służy do tworzenia i uruchamiania nowych zadań, metoda Wait działa podobnie jak operacja łączenia i czeka na zakończenie zadania.

Poniższy fragment kodu ilustruje, jak można użyć metody Task.Factory.StartNew.

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

Możesz również utworzyć zadanie przy użyciu metody Task.Run, jak pokazano w poniższym fragmencie kodu.

public async Task DoSomeWork()

        {

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

        }

        void TestMethod()

        {

            Console.WriteLine("Hello world!");

        }

Jeśli chcesz zwrócić wartość z zadania, możesz skorzystać z metody Task.FromResult, jak pokazano w poniższym fragmencie kodu.

public async Task DoSomeWork()

   {

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

   }

private string GetMessage()

   {

      return "Hello world!";

   }

Możesz także tworzyć zadania przy użyciu pełnomocnika lub akcji. Poniższy fragment kodu pokazuje, jak można tworzyć zadania przy użyciu akcji i delegatów.

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

task1.Start();

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

task2.Start();

Możesz także tworzyć zadania za pomocą lamba i metod anonimowych.

Task.Factory.StartNew i Task.Run

Task.Factory.StartNew to szybki sposób tworzenia i uruchamiania zadania. Należy zauważyć, że wywołanie metody Task.Factory.StartNew jest funkcjonalnie równoważne z utworzeniem instancji zadania, a następnie wywołaniem metody Start w instancji. Jednak z wielu powodów nie jest zalecane. Jeśli chcesz wykonać kod synchroniczny, Task.Factory.StartNew nie jest dobrym wyborem.

Należy pamiętać, że jeśli dostępny jest harmonogram zadań, metoda StartNew wykona zadanie w tym harmonogramie zadań. Wręcz przeciwnie, jeśli harmonogram nie jest dostępny, wykona zadanie w wątku puli wątków. Należy zauważyć, że Task.Factory.StartNew ma wartość domyślną TaskScheduler.Current, a nie TaskScheduler.Default.

Należy zauważyć, że wywołanie Task.Run (akcja) jest równoważne z następującą instrukcją: Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

Wręcz przeciwnie, wywołanie Task.Factory.StartNew (akcja) jest równoważne z następującą instrukcją:

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

Jeśli chcesz użyć Task.Factory.StartNew, jeśli utworzyłeś niestandardowy harmonogram zadań i jawnie przekazałeś do niego instancję harmonogramu. Zawsze polecałbym używanie Task.Run, ponieważ jest znacznie prostszy i ma bezpieczniejsze ustawienia domyślne. Innymi słowy, powinniśmy unikać korzystania z Task.Factory.StartNew, chyba że istnieje potrzeba utworzenia harmonogramu zadań, a następnie jawnego przekazania go podczas wywoływania metody StartNew w celu utworzenia nowego zadania i zaplanowania go. Jeśli chcesz skutecznie i niezawodnie używać metody TaskFactory.StartNew, powinieneś użyć niestandardowego harmonogramu zadań, a następnie określić CancellationToken i TaskCreationOptions.

Metoda Task.Run jest zalecana do użycia, gdy nie potrzebujesz bardzo szczegółowej kontroli nad harmonogramem wątków i jego zawiłościami. Należy używać Task.Run głównie w przypadku metod związanych z procesorem. Należy jednak używać Task.Run podczas wywoływania zadania, a nie w jego realizacji. Innymi słowy, powinieneś używać Task.Run nie wewnątrz żadnej implementacji metody, ale w punkcie, w którym metoda jest wywoływana. Na przykład poniższy fragment kodu jest przykładem „złego” fragmentu kodu.

public async Task DownloadDataFromWebAsync(Uri uri)

        {

            return await Task.Run(() =>

            {

                using (WebClient webClient = new WebClient())

                {

                    return webClient.DownloadString(uri);

                }

            });

        }

Zapoznaj się z fragmentem kodu podanym powyżej. Metoda nie jest skalowalna, ponieważ blokowałaby wątek w tle, pobierałby wątek z puli wątków i wykonywałby na nim synchronicznie. W związku z tym zużywałoby więcej zasobów w systemie.