Вопрос по Курсу: "Параллельное программирование с использованием MS VisualStudia 2010". При компиляции Самостоятельного задания (одновременная отрисовка прямоугольников, эллипсов и выдача в текст-бокс случайного числа) среда предупреждает: suspend - устаревшая команда; примените monitor, mutex и т.п. Создаётся впечатление, что Задание создано в более поздней среде, чем VS 2010. |
Введение в асинхронные задачи
Использование класса Task
С появлением версии .NET Framework 4.0 разработчики получили новую библиотеку для параллельного программирования - TPL и в частности - класс Task. Данный класс позволяет в значительной степени упростить написание параллельного кода, без необходимости работы, непосредственно с потоками или пулом потоков. От класса Thread класс Task отличается тем, что он является абстракцией, представляющей асинхронную операцию, а в классе Thread инкапсулируется поток исполнения.
Сами задачи создаются в виде объектов класса Task и создавать эти задачи можно различными способами:
- С использованием делегата Action и именного метода;
- С использованием анонимного делегата;
- С использованием лямбда-выражения и именного метода;
- С использованием лямбда-выражения и анонимного метода.
Для создания объектов класса Task используется следующий конструктор:
public Task(Action действие)
где действие обозначает точку входа в код, представляющий задачу, тогда как Action - делегат, определенный в пространстве имен System. Сама же форма делегата Action, выглядит следующим образом:
public delegate void Action();
Пример:
Task task = new Task (new Action(Print));
где Print - метод программы, который будет выполнен в отдельной задаче.
Класс TaskFactory
Класс TaskFactory кодирует некоторые распространенные шаблоны класса Task в методы, которые получают параметры по умолчанию, настраиваемые посредством своих конструкторов. В классе TaskFactory предоставляются различные методы, упрощающие создание задач и управление ими.
По умолчанию объект класса TaskFactory может быть получен из свойства Factory, доступного только для чтения в классе Task, используя это свойство, можно вызвать любые методы класса TaskFactory. Одним из таких методов - является метод StartNew(), у которого имеется множество форм вызова. Одна из таких форм продемонстрирована ниже:
public Task StartNew(Action действие)
где действие - точка входа в исполняемую задачу. Сначала в методе StartNew() автоматически создается экземпляр класса Task для действия, определяемого параметром действие, а затем планируется запуск задачи на исполнение. Следовательно, нет необходимости использовать метод Start(). Ниже представлены различные способы запуска задач с использованием TaskFactory:
// Использование фабрики задач TaskFactory taskfactory = new TaskFactory(); Task task = taskfactory.StartNew(Действие); // Использование фабрики задач через задачу Task task = Task.Factory.StartNew(Действие); // Использование конструктора Task Task task = new Task(Действие); task.Start();
Кроме использования обычного метода (делегат Action) в качестве объявления задачи, существует другой подход: указать лямбда-выражение как отдельно решаемую задачу.
Лямбда-выражения являются особой формой анонимных функций. Поэтому они могут исполняться как отдельные задачи. Лямбда-выражения оказываются особенно полезными в тех случаях, когда единственным назначением метода является решение одноразовой задачи:
// Вызов именного метода с помощью лямбда-выражения Task task = Task.Factory.StartNew(() => DoSomething()); // Вызов анонимного метода с помощью лямбда-выражения Task task = new Task.Factory.StartNew(() => { Console.WriteLine("Hello world!"); });
Примеры различных сортировок массивов с использованием принципа параллелизма
В данной части лекции будут рассмотрены различные виды алгоритмов сортировки массивов, а именно:
- Сортировка пузырьком;
- Сортировка вставками;
- Быстрая сортировка.
Ниже приведен пример программы, реализующей алгоритмы сортировки целочисленного случайно-сгенерированного массива в трех различных задачах, и последующем выводом на консоль, времени сортировки по каждому алгоритму:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; using System.Diagnostics; namespace SortsExample { //создаем класс SortResults реализующий интерфейс IDisposable class SortResults : IDisposable { //создаем объект класса Stopwatch, для измерения потраченного времени на работу алгоритмов сортировки private Stopwatch _stopwatch = new Stopwatch(); private string sortName; //создаем типизированную коллекцию List<string> static private List<string> propResult = new List<string>(); //cоздаем свойство возвращающая значение типа List<string> static public List<string> results { get { return propResult; } } //создаем конструктор класс SortResults в который будет передаваться название сортировки public SortResults(string name) { //присваиваем переменной sortName значение имени сортировки sortName = name; //запускаем таймер _stopwatch.Start(); } //создаем метод интерфейса IDisposable - Dispose, который будет вызываться по окончанию работы КАЖДОГО из алгоритмов сортировки public void Dispose() { //останавливаем таймер _stopwatch.Stop(); //добавляем в коллекцию результаты работы таймера в мс и название алгоритма results.Add(string.Format("{0} : {1:N0}ms", sortName, _stopwatch.ElapsedMilliseconds)); } } //создаем класс SortManager class SortManager { //создаем типизированную коллекцию List<int> private static List<int> integerList = new List<int>(); //создаем объект класса Barrier и задаем количество участвующих потоков в нашем случае это 3 private static Barrier sortBarrier = new Barrier(3); //создаем метод который будет генерировать случайный числа private static void PopulateIntegerList(uint size) { //создаем экземпляр класса Random Random randomNumber = new Random(DateTime.Now.Millisecond); for (int count = 0; count < size; count++) { //добавлем в коллекцию сгенерированное число integerList.Add(randomNumber.Next()); } } //создаем метод реализующий алгоритм быстрой сортировки public static void PivotSort(List<int> integerList, int start, int end, int pivot) { if (start < end) { pivot = integerList[end]; int location = start; int bound = end; while (location < bound) { if (integerList[location] < pivot) { location++; } else { integerList[bound] = integerList[location]; integerList[location] = integerList[bound - 1]; bound--; } } integerList[bound] = pivot; PivotSort(integerList, start, bound - 1, pivot); PivotSort(integerList, bound + 1, end, pivot); } } //создаем метод реализующий алгоритм сортировки вставками public static void InsertionSort(List<int> sortedList) { int count = sortedList.Count; int currentLocation, currentValue, insertionLocation; sortedList.Insert(0, 0); for (int location = 1; location < count + 1; location++) { currentLocation = location; insertionLocation = location - 1; currentValue = sortedList[currentLocation]; while (sortedList[insertionLocation] > currentValue) { sortedList[currentLocation] = sortedList[insertionLocation]; currentLocation--; insertionLocation--; } sortedList[currentLocation] = currentValue; } sortedList.Remove(0); } //создаем метод реализующий алгоритм сортировки пузырьком public static void BubbleSort(List<int> sortedList) { int count = sortedList.Count; int temporary; for (int iteration = 0; iteration < count; iteration++) { for (int index = 0; index + 1 < count; index++) { if (sortedList[index] > sortedList[index + 1]) { temporary = sortedList[index]; sortedList[index] = sortedList[index + 1]; sortedList[index + 1] = temporary; } } } } public static void DoSort() { //передаем в метод PopulateIntegerList, значение соответствующее количеству сгенерированных чисел PopulateIntegerList(100000); //создаем типизированную коллекцию List<int>, которая является копией сгенерированного массива integerList и будет использоваться для сортировки пузырьком List<int> bubbleList = integerList.ToList(); //создаем задачу сортировки пузырьком Task taskBubbleSort = new Task(() => { //вызываем метод SignalAndWait для синхронизации потока sortBarrier.SignalAndWait(); //создаем объект класса SortResults using (new SortResults("Сортировка пузырьком")) { //передаем значение коллекции bubbleList в метод сортировки пузырьком BubbleSort BubbleSort(bubbleList); } }); //запускаем задачу taskBubbleSort.Start(); //создаем типизированную коллекцию List<int>, которая является копией сгенерированного массива integerList и будет использоваться для сортировки вставками List<int> insertionList = integerList.ToList(); //создаем задачу сортировки вставками Task taskInsertionSort = new Task(() => { //вызываем метод SignalAndWait для синхронизации потока sortBarrier.SignalAndWait(); //создаем объект класса SortResults using (new SortResults("Сортировка вставками")) { //передаем значение коллекции insertionList в метод сортировки вставками InsertionSort InsertionSort(insertionList); } }); //запускаем задачу taskInsertionSort.Start(); //создаем типизированную коллекцию List<int>, которая является копией сгенерированного массива integerList и будет использоваться для быстрой сортировки List<int> pivotList = integerList.ToList(); //создаем задачу быстрой сортировки Task taskPivotSort = new Task(() => { //вызываем метод SignalAndWait для синхронизации потока sortBarrier.SignalAndWait(); //создаем объект класса SortResults using (new SortResults("Быстрая сортировка")) { //передаем значение коллекции pivotList в метод быстрой сортировки пузырьком PivotSort PivotSort(pivotList, 0, pivotList.Count - 1, 1); } }); //запускаем задачу taskPivotSort.Start(); // ожидаем завершения всех задач Task.WaitAll(new Task[] { taskBubbleSort, taskInsertionSort, taskPivotSort }); //выводим результат на экран foreach (string result in SortResults.results) { Console.WriteLine(result); } Console.ReadLine(); } } class Program { static void Main(string[] args) { //вызываем метод класса SortManager - DoSort SortManager.DoSort(); } } }
Результат выполнения программы отображен на Рис. 5.1, который показывает, что массив, состоящий из 100000 случайных чисел, быстрее будет отсортирован алгоритмом "быстрой сортировки" нежели другими алгоритмами.