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#.
На связи был Алексей Гулынин, оставляйте свои комментарии, увидимся в следующих статьях.