Вопрос по Курсу: "Параллельное программирование с использованием MS VisualStudia 2010". При компиляции Самостоятельного задания (одновременная отрисовка прямоугольников, эллипсов и выдача в текст-бокс случайного числа) среда предупреждает: suspend - устаревшая команда; примените monitor, mutex и т.п. Создаётся впечатление, что Задание создано в более поздней среде, чем VS 2010. |
Параллельные коллекции
Пример (Рис. 14.1) реализации интерфейса IProducerConsumerCollection<T> представлен в примере ниже:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Collections.Concurrent; using System.Collections; namespace IProducerConsumerCollection { //Реализуем интерфейс IProducerConsumerCollection<T> в классе ProducerConsumerCollection<T> class ProducerConsumerCollection<T> : IProducerConsumerCollection<T> { private IProducerConsumerCollection<T> _queue = new ConcurrentQueue<T>(); public void CopyTo(T[] array, int index) { Console.WriteLine("Вызов метода CopyTo"); _queue.CopyTo(array, index); } public T[] ToArray() { Console.WriteLine("Вызов метода ToArray"); return _queue.ToArray(); } public bool TryAdd(T item) { Console.WriteLine("Вызов метода TryAdd " + item.ToString()); return _queue.TryAdd(item); } public bool TryTake(out T item) { var ret = _queue.TryTake(out item); var val = ""; if (item == null) { val = "NULL"; } else { val = item.ToString(); } Console.WriteLine("Вызов метода TryTake возрвращает объект коллекции " + val + " метод возвращает значение " + ret.ToString()); return ret; } public void CopyTo(Array array, int index) { _queue.CopyTo(array, index); } public IEnumerator<T> GetEnumerator() { Console.WriteLine("GetEnumerator T.."); return _queue.GetEnumerator(); } IEnumerator System.Collections.IEnumerable.GetEnumerator() { Console.WriteLine("GetEnumerator.."); return _queue.GetEnumerator(); } public int Count { get { Console.WriteLine("Вызов метода Count..."); return _queue.Count; } } public bool IsSynchronized { get { return _queue.IsSynchronized; } } public object SyncRoot { get { return _queue.SyncRoot; } } } class Program { static void Main(string[] args) { ProducerConsumerCollection<string> _prodConsumer = new ProducerConsumerCollection<string>(); //создаем объект класса ProducerConsumerCollection _prodConsumer.TryAdd("Значение 1 коллекции");//добавляем значения _prodConsumer.TryAdd("Значение 2 коллекции");//в коллекцию _prodConsumer.TryAdd("Значение 3 коллекции"); _prodConsumer.TryAdd("Значение 4 коллекции"); string s; _prodConsumer.TryTake(out s); _prodConsumer.TryTake(out s); var array = _prodConsumer.ToArray(); for (int n = 0; n < array.Length; n++) { Console.WriteLine(array[n]); } Console.ReadLine(); } } }
увеличить изображение
Рис. 14.1. Результат работы программы с использованием интерфейса IProducerConsumerCollection<T>
Пример использования обычной коллекции с применением параллелизма
Один из наиболее распространенных способов разделения данных - это использование коллекций. Применение параллельного программирования к коллекциям содержащим большой массив данных: позволит в значительной степени увеличить скорость обработки алгоритма(ов). При этом разработчик может столкнуться с проблемой "соревнования" потоков за доступ к данным коллекции. Пример ниже демонстрирует использование коллекции Queue<int> которая содержит 1000 элементов и элементы которой обрабатывает массив из 10 задач. Каждая задача асинхронно удаляет первый элемент коллекции, и увеличивает значении переменной-счетчика, используя для синхронизации атомарную операцию Interlocked.Increment()
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Collections.Concurrent; using System.Collections; namespace ConcurrentCollection { class Program { static void Main(string[] args) { // создаем коллекцию Queue<int> sharedQueue = new Queue<int>(); // заполняем коллекцию в цикле с помощью метода Enqueue for (int i = 0; i < 1000; i++) { sharedQueue.Enqueue(i); } // объявляем переменную-счетчик количества обработанных элементов int itemCount = 0; // создаем список задач Task[] tasks = new Task[10]; for (int i = 0; i < tasks.Length; i++) { // создаем задачу tasks[i] = new Task(() => { while (sharedQueue.Count > 0) { // удаляем элемент из коллекции с помощью метода Dequeue int item = sharedQueue.Dequeue(); // увеливам значение переменной и сохраняем результат Interlocked.Increment(ref itemCount); } }); // запускаем новую задачу tasks[i].Start(); } // ожидаем завершения всех задач Task.WaitAll(tasks); // выводим на экран отчет о количестве обработанных элементов Console.WriteLine("Обработанно элементов: {0}", itemCount); Console.ReadLine(); } } }
увеличить изображение
Рис. 14.2. Результат работы программы с использованием коллекции Queue<int> и многозадачности
Коллекция Queue<int> не является потокобезапассной, поэтому в случае рассинхронизации потоков (метод Thread.Sleep()), может возникнуть исключение InvalidOperationException (Рис. 14.3). Это исключение возникнет, когда один из потоков попытается вызвать метод Dequeue(), при этом сама коллекция будет уже пустой.