не хватает одного параметра: static void Main(string[] args) |
Распараллеливание циклов. Класс Parallel
Метод Parallel.Invoke
Нам осталось рассмотреть третий и последний метод класса Parallel - метод Invoke. Это самый простой по синтаксису и по семантике метод. У него всего две реализации. Синтаксис основной реализации имеет вид:
public static void Parallel.Invoke(params Action[] actions)
Поскольку параметр actions объявлен с описателем params, то фактический параметр может представлять список с произвольным числом элементов. Каждый элемент этого списка задает имя метода, сигнатура которого удовлетворяет делегату Action. Никакие параметры методу не передаются. Если же методу необходимо передать информацию, то всегда можно использовать тот факт, что любой вызов метода всегда имеет цель - объект, вызывающий метод. Поэтому методу Invoke в качестве аргумента можно передавать некоторый объект, вызывающий метод без параметров. Вся необходимая информация, как входная, так и выходная, создаваемая вызываемым методом, передается через поля объекта.
Все методы, переданные при вызове Invoke, могут выполняться параллельно и в произвольном порядке. Что происходит, когда один из методов заканчивает свою работу нормальным образом или, не дай бог, в результате выполнения исключительной ситуации? Все остальные методы продолжат свою работу, в том числе будут запущены на выполнение все еще не начавшие работать методы, ждущие своей очереди. Метод Invoke заканчивает свое выполнение только тогда, когда закончат работу все методы из переданного ему списка. Все возникшие исключительные ситуации собираются в одну ситуацию - AggregateException, - которая и выбрасывается в этом случае по завершении работы метода Invoke. Естественно, необходимо предусматривать обработчик этой ситуации.
Рассмотрим пример, моделирующий ситуацию с вызовом Invoke. Вспомним сказку о Золушке, которая не могла поехать на бал, поскольку злая мачеха, поручила ей столько работ, что сама Золушка, работая последовательно, никак не могла бы с ними справиться до начала бала. Благодаря фее, появились дополнительные процессоры, выполняющие работы параллельно. В результате, все работы были выполнены, а Золушка смогла попасть на бал и очаровать принца. Вот модель этой сказочной истории:
/// <summary> /// Работы Золушки /// </summary> class Zolushka_Jobs { delegate void Job(); Job[] jobs; string[] messages; Prince prince = new Prince("Гарри"); public Zolushka_Jobs() { const int N = 5; messages = new string[N]; jobs = new Job[N]; jobs[0] = Job_One; jobs[1] = Job_Two; jobs[2] = Job_Three; jobs[3] = Job_Four; jobs[4] = prince.Love; } public string[] Messages { get { return messages; } }
Добавим теперь в класс методы, моделирующие работы Золушки:
void Job_One() { messages[0] = "Пол вымыт!"; } void Job_Two() { messages[1] = "Посуда вымыта, вычищена и сверкает!"; } void Job_Three() { messages[2] = "Фасоль отсортирована!"; } void Job_Four() { messages[3] = "Принц очарован!"; }
Следующий метод запускает параллельное выполнение всех работ:
public void Make_All_Jobs() { Parallel.Invoke(Job_One, Job_Two, Job_Three, Job_Four, prince.Love); // Parallel.For(0, 5, Make); messages[4] = prince.Message; }
Обратите внимание, первые четыре метода, передаваемые Invoke, это методы класса Zolushka. Для них целью является текущий объект этого класса (this). Но параллельно будет выполняться и метод другого класса - метод Love из класса Prince, вызываемый объектом prince этого класса. Это демонстрирует возможность в одном вызове Invoke параллельно исполнять методы разных классов.
Метод Invoke аналогичен параллельному циклу, его можно заменить оператором For. Можно создать массив работ jobs, как это сделано в конструкторе класса Zolushka, а затем запустить работы на выполнение, выполнив вызов:
Parallel.For(0, 5, Make);
Этот вызов включен в процедуру Make_All_Jobs. Он закомментирован, но вызовы Invoke и For дают эквивалентные результаты.
Приведу текст метода Make:
void Make(int i) { jobs[i](); }
Для полноты картины приведу текст класса Prince с методом Love:
class Prince { string name; string message; public string Message { get { return message; } } public Prince(string name) { this.name = name; } public void Love() { message = String.Format("Я, принц {0}, влюбился в Золушку!", name); } }
А вот текст главного метода Main, оживляющего нашу модель:
static void Main(string[] args) { Zolushka_Jobs zj = new Zolushka_Jobs(); zj.Make_All_Jobs(); for (int i = 0; i < zj.Messages.Length; i++) Console.WriteLine(zj.Messages[i]); }
При запуске проекта на консоль выводятся следующие результаты: