Урок 8. Создание параллельных задач C#
На восьмом уроке учебника по параллельному программированию в .NET мы займемся изучением класса задач (Task). Он позволяет программисту явно создавать задачи, предназначенные для параллельного выполнения. В этой статье объясняется, как создаются и выполняются задачи.
Задачи
В предыдущей части этого урока мы рассмотрели метод Parallel.Invoke. Он позволяет неявно генерировать набор задач, которые могут выполняться параллельно. Это полезный метод, но он мало влияет на выполнение отдельных задач и порядок их выполнения. Когда вам требуется больше контроля над параллельными задачами, вы можете использовать класс задач (Tasks). Он позволяет явно генерировать параллельные задачи. Код, необходимый для явного создания задачи, немного сложнее, чем для Parallel.Invoke, но преимущества перевешивают этот недостаток.
Класс задач может выполнять те же функции, что и Parallel.Invoke. Кроме того, могут быть созданы следующие типы задач:
- Задачи продолжения. Задача продолжения заменяет использование обратных вызовов, которые могут потребоваться при использовании других методов параллелизма и многопоточности. Этот тип задач настраивается на запуск только после завершения другой задачи или группы задач. Они могут безопасно использовать данные, созданные этими более ранними задачами.
- Вложенные и дочерние задачи. Вложенные задачи - это просто задачи, которые создаются в рамках другой задачи, но остаются независимыми. Дочерние задачи аналогичны тем, которые создаются внутри родительской задачи. Однако они более тесно связаны с этим родителем.
- Задачи с возвращаемыми значениями. При использовании явных задач задача может выполняться параллельно и все же возвращать значение, которое может быть использовано после завершения задачи.
Примеры, приведенные в этой статье, могут быть выполнены в консольном приложении. Поскольку класс Tasks находится в System.Threading.Tasks, и мы также будем использовать класс Thread из System.Threading пространства имен, вы должны добавить следующие директивы using перед выполнением примеров.
using System.Threading; using System.Threading.Tasks;Выполнение параллельных задач
Класс Task предоставляет оболочку для делегата действия. Делегат описывает код, который вы хотите выполнить, а оболочка обеспечивает параллелизм и включает функции, упомянутые выше. Простой способ создать задачу - использовать конструктор с единственным параметром, который принимает делегат, который вы хотите выполнить. Во многих случаях этот делегат определяется как лямбда-выражение. Задачи не выполняются сразу после их создания. Для начала задачи необходимо вызвать метод Start.
Следующий код имитирует шаги, которые могут быть выполнены при запуске приложения управления взаимоотношениями с клиентами (CRM). Первоначально отображается сообщение о том, что приложение запускается. Далее определяются две задачи. Первый имитирует загрузку пользовательских данных из базы данных. Второй имитирует получение данных о клиентах. Задачи запускаются двумя вызовами метода Start перед отображением сообщения о загрузке приложения CRM.
Console.WriteLine("CRM Application Starting"); Task loadUserDataTask = new Task(() => { Console.WriteLine("Loading User Data"); Thread.Sleep(2000); Console.WriteLine("User data loaded"); }); Task loadCustomerDataTask = new Task(() => { Console.WriteLine("Loading Customer Data"); Thread.Sleep(2000); Console.WriteLine("Customer data loaded"); }); loadUserDataTask.Start(); loadCustomerDataTask.Start(); Console.WriteLine("CRM Application Loaded"); Console.ReadLine(); loadUserDataTask.Dispose(); loadCustomerDataTask.Dispose(); /* OUTPUT CRM Application Starting CRM Application Loaded Loading User Data Loading Customer Data User data loaded Customer data loaded */Приведенный выше код показывает общий шаблон как для параллельных, так и для многопоточных приложений. Код будет выполняться в потоке пользовательского интерфейса (UI), который может блокировать ввод данных от пользователя и создавать впечатление, что программа перестала отвечать на запросы. Запуская задачи поиска данных в отдельных потоках, мы позволяем программе реагировать еще до того, как информация будет получена. Это дает лучший пользовательский опыт до тех пор, пока мы не позволяем функциям, которые полагаются на данные, запускаться до их появления. Из выходных данных видно, что приложение загружено и готово к использованию, в то время как доступ к данным продолжается в фоновом режиме.
Вы также можете видеть из выходных данных в комментариях, что параллельные задачи были запущены после отображения сообщения " CRM Application Loaded". Важно понимать, что при вызове метода Start задача может начаться немедленно. Однако если все процессорные ядра заняты, выполнение задачи может быть отложено до тех пор, пока она не будет эффективно выполнена. Как и другие элементы библиотеки параллельных задач, задачи обеспечивают потенциальный, а не гарантированный параллелизм.
Примечание: если вы нажмете Enter, чтобы пропустить Console.ReadLine до завершения задач вы столкнетесь с исключением. Это вызвано вызовом Dispose, так как объекты задачи не могут быть удалены во время выполнения.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.
Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.