C# 4.0: полное руководство - Герберт Шилдт
Шрифт:
Интервал:
Закладка:
// Продемонстрировать запуск нового процесса.
using System;
using System.Diagnostics;
class StartProcess {
static void Main() {
Process newProc = Process.Start("wordpad.exe");
Console.WriteLine("Новый процесс запущен.");
newProc.WaitForExit();
newProc.Close(); // освободить выделенные ресурсы
Console.WriteLine("Новый процесс завершен.");
}
}
При выполнении этой программы запускается стандартное приложение WordPad, и на экране появляется сообщение "Новый процесс запущен. ". Затем программа ожидает закрытия WordPad. По окончании работы WordPad на экране появляется заключительное сообщение "Новый процесс завершен.".
ГЛАВА 24 Многопоточное программирование. Часть вторая: библиотека TPL
Вероятно, самым главным среди новых средств, внедренных в версию 4.0 среды .NET Framework, является библиотека распараллеливания задач (TPL). Эта библиотека усовершенствует многопоточное программирование двумя основными способами. Во-первых, она упрощает создание и применение многих потоков. И во-вторых, она позволяет автоматически использовать несколько процессоров. Иными словами, TPL открывает возможности для автоматического масштабирования приложений с целью эффективного использования ряда доступных процессоров. Благодаря этим двух особенностям библиотеки TPL она рекомендуется в большинстве случаев к применению для организации многопоточной обработки.
Еще одним средством параллельного программирования, внедренным в версию 4.0 среды .NET Framework, является параллельный язык интегрированных запросов (PLINQ). Язык PLINQ дает возможность составлять запросы, для обработки которых автоматически используется несколько процессоров, а также принцип параллелизма, когда это уместно. Как станет ясно из дальнейшего, запросить параллельную обработку запроса очень просто. Следовательно, с помощью PLINQ можно без особого труда внедрить параллелизм в запрос.
Главной причиной появления таких важных новшеств, как TPL и PLINQ, служит возросшее значение параллелизма в современном программировании. В настоящее время многоядерные процессоры уже стали обычным явлением.
Кроме того, постоянно растет потребность в повышении производительности программ. Все это, в свою очередь, вызвало растущую потребность в механизме, который позволял бы с выгодой использовать несколько процессов для повышения производительности программного обеспечения. Но дело в том, что в прошлом это было не так-то просто сделать ясным и допускающим масштабирование способом. Изменить это положение, собственно, и призваны TPL и PLINQ. Ведь они дают возможность легче (и безопаснее) использовать системные ресурсы.
Библиотека TPL определена в пространстве имен System.Threading.Tasks. Но для работы с ней обычно требуется также включать в программу класс System.Threading, поскольку он поддерживает синхронизацию и другие средства многопоточной обработки, в том числе и те, что входят в класс Interlocked.
В этой главе рассматривается и TPL, и PLINQ. Следует, однако, иметь в виду, что и та и другая тема довольно обширны. Поэтому в этой главе даются самые основы и рассматриваются некоторые простейшие способы применения TPL и PLINQ. Таким образом, материал этой главы послужит вам в качестве удобной отправной точки для дальнейшего изучения TPL и PLINQ. Если параллельное программирование входит в сферу ваших интересов, то именно эти средства .NET Framework вам придется изучить более основательно.
-------------------------------
ПРИМЕЧАНИЕ
Несмотря на то что применение TPL и PLINQ рекомендуется теперь для разработки большинства многопоточных приложений, организация многопоточной обработки на основе класса Thread, представленного в главе 23, по-прежнему находит широкое распространение. Кроме того, многое из того, что пояснялось в главе 23, применимо и к TPL. Поэтому усвоение материала главы 23 все еще необходимо для полного овладения особенностями организации многопоточной обработки на С#.
-------------------------------
Два подхода к параллельному программированию
Применяя TPL, параллелизм в программу можно ввести двумя основными способами. Первый из них называется параллелизмом данных. При таком подходе одна операция над совокупностью данных разбивается на два параллельно выполняемых потока или больше, в каждом из которых обрабатывается часть данных. Так, если изменяется каждый элемент массива, то, применяя параллелизм данных, можно организовать параллельную обработку разных областей массива в двух или больше потоках. Нетрудно догадаться, что такие параллельно выполняющиеся действия могут привести к значительному ускорению обработки данных по сравнению с последовательным подходом. Несмотря на то что параллелизм данных был всегда возможен и с помощью класса Thread, построение масштабируемых решений средствами этого класса требовало немало усилий и времени. Это положение изменилось с появлением библиотеки TPL, с помощью которой масштабируемый параллелизм данных без особого труда вводится в программу.
Второй способ ввода параллелизм называется параллелизмом задач. При таком подходе две операции или больше выполняются параллельно. Следовательно, параллелизм задач представляет собой разновидность параллелизма, который достигался в прошлом средствами класса Thread. А к преимуществам, которые сулит применение TPL, относится простота применения и возможность автоматически масштабировать исполнение кода на несколько процессоров.
Класс Task
В основу TPL положен класс Task. Элементарная единица исполнения инкапсулируется в TPL средствами класса Task, а не Thread. Класс Task отличается от класса Thread тем, что он является абстракцией, представляющей асинхронную операцию. А в классе Thread инкапсулируется поток исполнения. Разумеется, на системном уровне поток по-прежнему остается элементарной единицей исполнения, которую можно планировать средствами операционной системы. Но соответствие экземпляра объекта класса Task и потока исполнения не обязательно оказывается взаимно-однозначным. Кроме того, исполнением задач управляет планировщик задач, который работает с пулом потоков. Это, например, означает, что несколько задач могут разделять один и тот же поток. Класс Task (и вся остальная библиотека TPL) определены в пространстве имен System.Threading.Tasks.
Создание задачиСоздать новую задачу в виде объекта класса Task и начать ее исполнение можно самыми разными способами. Для начала создадим объект типа Task с помощью конструктора и запустим его, вызвав метод Start(). Для этой цели в классе Task определено несколько конструкторов. Ниже приведен тот конструктор, которым мы собираемся воспользоваться:
public Task(Action действие)
где действие обозначает точку входа в код, представляющий задачу, тогда как Action — делегат, определенный в пространстве имен System. Форма делегата Action, которой мы собираемся воспользоваться, выглядит следующим образом.
public delegate void Action()
Таким образом, точкой входа должен служить метод, не принимающий никаких параметров и не возвращающий никаких значений. (Как будет показано далее, делегату Action можно также передать аргумент.)
Как только задача будет создана, ее можно запустить на исполнение, вызвав метод Start(). Ниже приведена одна из его форм.
public void Start()
После вызова метода Start() планировщик задач запланирует исполнение задачи. В приведенной ниже программе все изложенное выше демонстрируется на практике. В этой программе отдельная задача создается на основе метода MyTask(). После того как начнет выполняться метод Main(), задача фактически создается и запускается на исполнение. Оба метода MyTask() и Main() выполняются параллельно.
// Создать и запустить задачу на исполнение.
using System;
using System.Threading;
using System.Threading.Tasks;
class DemoTask {
static void MyTask() {
Console.WriteLine("MyTask() запущен");
for(int count = 0; count < 10; count++) {
Thread.Sleep(500);
Console.WriteLine("В методе MyTask(), подсчет равен " + count);