Task c#

Task c#

Всем доброго времени суток. На связи Алексей Гулынин. В прошлой статье мы написали простую веб-службу. В данной статье я бы хотел разобрать класс Task в C#. С помощью данного класса мы можем задйествовать параллельный код. Для чего он нужен? В настоящее время многие процессоры являются многоядерными, и если есть необходимость в использовании нескольких ядер, нужно этим пользоваться.

Допустим мы используем графический интерфейс для нашего приложения, и у нас есть кнопка, по нажатию на которую выполняется какой-то сложный запрос к базе данных. После нажатия на эту кнопку приложение будет висеть до тех пор, пока этот запрос не отработает. В окне будет написано "Не отвечает" или "Not responding". Для решения таких задач как раз и существует параллельный код, которые выполняет код в различных потоках.

С .NET Framework 4 выходит высокоуровневая библиотека, которая называется TPL ("Task Paralel Library"). В данной библиотеке находятся классы, которые позволяют запускать задачи параллельно. TPL — это абстракция, в которой мы оперируем понятием задач, а не потоков (примечание: работа с потоками — это низкоуровневые операции).

В .NET Framework 4.5 появились ключевые слова "async" и "await", которые упрощают задачу написания асинхронных методов.

Разберем как создаются задачи (Task):

// new Action(myMethod) - это делегат
Task myTask = new Task(new Action(myMethod));

Создаём задачу, используя Action Delegate:

private static void GetCurrentTime()
{
   Console.WriteLine("Текущее время: {0}", DateTime.Now);
}
static void Main(string[] args)
{
   Task myTask = new Task(new Action(GetCurrentTime));
   // Запускаем задачу
   myTask.Start();
   Console.ReadLine();
}

Создаём задачу, используя анонимный делегат (Anonymous Delegate):

static void Main(string[] args)
{
  Task myTask = new Task(delegate { Console.WriteLine("Текущее время: {0}", DateTime.Now); });
  myTask.Start();
  Console.ReadLine();
}

Используя лямбда-выражения (тоже самое, что анонимный делегат, только запись короче):

Task myTask = new Task(() => { Console.WriteLine("Текущее время: {0}", DateTime.Now); });

В круглых скобках указываются параметры, если параметров нет — то пустые. Как только мы запускаем метод "Start()" — это означает, что данная задача заработала параллельно нашему основному потоку.

Следующим способом можно создать задачу и сразу запустить её:

Task.Factory.StartNew(() => { Console.WriteLine("Текущее время: {0}", DateTime.Now); });

"Factory" — это статическое поле класса "Task", у которого есть метод "StartNew()".

Когда мы запускаем задачу в отдельном потоке, основной поток не ждёт пока завершится эта задача. Очень часто бывает нужно, чтобы задача в дополнительном потоке завершилась, и уже после этого продолжилась работа в основном потоке. Делается это с помощью метода "Wait()". Текущий поток приостанавливается, до тех пор пока задача не завершится:

myTask.Wait();

Метод "WaitAll(params Task[] tasks)" дожидается когда завершатся все задачи из массива.

"WaitAny()" дожидается, когда завершится хотя бы одна задача из массива.

static void Main(string[] args)
{
   Task[] myTasks = new Task[3] 
   {
      Task.Factory.StartNew(() => {Console.WriteLine("Создали 1 задачу");}),
      Task.Factory.StartNew(() => {Console.WriteLine("Создали 2 задачу");}),
      Task.Factory.StartNew(() => {Console.WriteLine("Создали 3 задачу");})
   };
   // Ждём, когда завершится хотя бы одна задача
   Task.WaitAny(myTasks);
   // Ждём, когда завершатся все задачи
   Task.WaitAll(myTasks);
   Console.ReadLine();
}

Пока мы рассмотрели методы, которые ничего не возвращают. Как быть в том случае, когда нужно вернуть какое-то значение? Здесь используется конструкция "Task":

// Тип возвращаемого значения указывается в угловых скобках
Task<string> myTask = Task.Factory.StartNew<string>(() => DateTime.Now.Year.ToString());
// или можно вот так:
// Task<string> myTask = Task.Factory.StartNew<string>(() => { return DateTime.Now.Year.ToString(); });
// Свойство Result как раз и содержит результат выполнения задачи
Console.WriteLine("Текущий год: {0}", myTask.Result);

Когда мы запрашиваем свойство Result, то перед этим не нужно вызывать метод Wait(), т.к. он вызывается автоматически.

В данной статье вы узнали, как работать с классом Task в C#.

На связи был Алексей Гулынин, оставляйте свои комментарии, увидимся в следующих статьях.


Комментарии:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *