На методите Task.Factory.StartNew и Task.Run

Когато създавате задачи с помощта на методите Task.Factory.StartNew или Task.Run, трябва да имате предвид някои важни моменти, когато пишете асинхронен код. В повечето случаи е препоръчително да избягвате използването на метода Task.Factory.StartNew, ако работите с асинхронен код. Ако работите с паралелен код, бих казал, че StartNew е добър избор.

Планировчикът на задачи е компонент, който отговаря за планирането на задачите; Рамката .Net ви предоставя два планиращи задачи. Има планировчик на задачи по подразбиране, който се изпълнява в пула от нишки на .Net framework, и има планировчик на задачи, който се изпълнява в контекста на синхронизация на определена цел. Планировката на задачи по подразбиране ще бъде достатъчна през повечето време, но можете също да създадете свой собствен планировчик на задачи, за да осигурите добавени функционалности. За да създадете свой собствен график за планиране на задачи, ще трябва да създадете клас, който разширява класа System.Threading.Tasks.TaskScheduler.

Как да създам задачи с помощта на библиотеката за паралелни задачи?

Има няколко начина, по които можете да създавате и стартирате задачи в .Net. Трябва да използвате класа System.Threading.Tasks.Task или System.Threading.Tasks.Task, за да създавате задачи (планируема единица работа). Докато първият се използва за създаване на задача, която не връща стойност, втората се използва за създаване на задачи, които имат върнати стойности. Свойството Task.Factory е екземпляр на класа TaskFactory. Това свойство се използва за създаване и планиране на задачи. Докато методът Task.Factory.StartNew работи като операция с разклонение и се използва за създаване и стартиране на нови задачи, методът Wait работи точно като операция за присъединяване и чака задачата да бъде завършена.

Следният кодов фрагмент илюстрира как можете да използвате метода Task.Factory.StartNew.

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

Можете също да създадете задача, като използвате метода Task.Run, както е показано в кодовия фрагмент по-долу.

public async Task DoSomeWork()

        {

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

        }

        void TestMethod()

        {

            Console.WriteLine("Hello world!");

        }

Ако искате да върнете стойност от задача, можете да се възползвате от метода Task.FromResult, както е показано в кодовия фрагмент по-долу.

public async Task DoSomeWork()

   {

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

   }

private string GetMessage()

   {

      return "Hello world!";

   }

Можете също да създавате задачи, като използвате делегат или действие. Следният кодов фрагмент показва как можете да създавате задачи, като използвате действия и делегати.

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

task1.Start();

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

task2.Start();

Можете също така да създавате задачи, използвайки lamba и анонимни методи.

Task.Factory.StartNew и Task.Run

Task.Factory.StartNew е бърз начин за създаване и стартиране на задача. Имайте предвид, че извикването на Task.Factory.StartNew е функционално еквивалентно на създаване на екземпляр на задача и след това извикване на метода Start в екземпляра. Не се препоръчва обаче да се използва поради изобилие от причини. Ако искате да изпълните синхронен код, Task.Factory.StartNew не е добър избор.

Имайте предвид, че ако е наличен планировчик на задачи, методът StartNew ще изпълни задачата на този планировчик на задачи. Напротив, ако планировчикът не е наличен, той ще изпълни задачата върху нишка на пула от нишки. Трябва да се отбележи, че Task.Factory.StartNew по подразбиране е TaskScheduler.Current, а не TaskScheduler.Default.

Обърнете внимание, че извикването на Task.Run (действие) е еквивалентно на следния израз: Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

Напротив, извикване на Task.Factory.StartNew (действие) е еквивалентно на следното изявление:

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

Ако може да искате да използвате Task.Factory.StartNew, ако сте създали персонализиран планировчик на задачи и му предадете екземпляра на планиращия изрично. Винаги бих препоръчал използването на Task.Run, тъй като е много по-опростен и има по-безопасни настройки по подразбиране. С други думи, трябва да избягваме използването на Task.Factory.StartNew, освен ако не е необходимо да се създаде програма за планиране на задачи и след това да се предаде изрично при извикване на метода StartNew за създаване на нова задача и планиране. Ако трябва да използвате метода TaskFactory.StartNew ефективно и надеждно, трябва да използвате персонализиран планировчик на задачи и след това да посочите CancellationToken и TaskCreationOptions.

Методът Task.Run се препоръчва да се използва, когато не е необходимо да имате много фин контрол върху планирането на конци и неговите тънкости. Трябва да използвате Task.Run предимно на методи, свързани с процесора. Трябва обаче да използвате Task.Run, докато извиквате задачата, а не вътре в изпълнението на задачата. С други думи, трябва да използвате Task.Run не във всяка реализация на метод, а в точката, където методът е извикан. Като пример, следният кодов фрагмент е пример за „лош“ код.

public async Task DownloadDataFromWebAsync(Uri ури)

        {

            return await Task.Run(() =>

            {

                using (WebClient webClient = new WebClient())

                {

                    return webClient.DownloadString(ури);

                }

            });

        }

Обърнете се към кодовия фрагмент, даден по-горе. Методът не е мащабируем, тъй като би блокирал фоновата нишка, извлича нишка от пула от нишки и изпълнява синхронно върху нея. Следователно, тя ще консумира повече ресурси във вашата система.