Опубликован: 23.01.2013 | Уровень: для всех | Доступ: платный | ВУЗ: Томский политехнический университет
Лекция 11:

Параллельные коллекции. Низкоуровневая синхронизация

ConcurrentStack

ConcurrentStack представляет собой потокобезопасную коллекцию, обслуживаемую по принципу "последним поступил - первым обслужен" (LIFO). Данная коллекция похожа на коллекцию ConcurrentQueue, но с другими методами доступа к элементам. Класс ConcurrentStack определяет методы Push(), PushRange(), TryPeek(), TryPop() и TryPopRange(). Более подробно методы класса ConcurrentStack описаны в Табл. 15.4.

Таблица 15.4. Основные методы коллекции ConcurrentStack
Имя Описание
Push(T) Добавляет объект в качестве верхнего элемента коллекции.
PushRange(T[]) Добавляет неделимым блоком несколько объектов в качестве верхнего элемента коллекции.
PushRange(T[], int, int) Добавляет неделимым блоком несколько объектов в качестве верхнего элемента коллекции.
TryPeek(out T) Пытается вернуть объект из начала коллекции ConcurrentStack без его удаления.
TryPop(out T) Пытается извлечь и вернуть верхний объект коллекции ConcurrentStack.
TryPopRange(out T[]) Пытается извлечь и вернуть несколько объектов из начала коллекции ConcurrentStack в виде неделимого блока.
TryPopRange(out T[], int, int) Пытается извлечь и вернуть несколько объектов из начала коллекции ConcurrentStack в виде неделимого блока.

Пример использования коллекции ConcurrentStack представлен ниже:

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 ConcurrentStackExample
{
    class Program
    {
    
        static void Main(string[] args)
        {
            // создаем коллекцию
            ConcurrentStack<int> sharedStack = new ConcurrentStack<int>();
            // заполняем коллекцию в цикле с помощью метода Push
            for (int i = 0; i < 1000; i++)
            {
                sharedStack.Push(i);
            }
            // объявляем переменную-счетчик количества обработанных элементов
            int itemCount = 0;
            // создаем список задач
            Task[] tasks = new Task[10];
            for (int i = 0; i < tasks.Length; i++)
            {
                // создаем задачу
                tasks[i] = new Task(() =>
                {
                    while (sharedStack.Count > 0)
                    {
                        Thread.Sleep(10);
                        int queueElement;
                        // удаляем элемент из коллекции с помощью метода TryPop
                        bool gotElement = sharedStack.TryPop(out queueElement);
                        // увеличиваем значение переменной и сохраняем результат
                        if (gotElement)
                        {                   
                        Interlocked.Increment(ref itemCount); 
                        } 
                    }
                });
                // запускаем новую задачу
                tasks[i].Start();
            }
                // ожидаем завершения всех задач
                Task.WaitAll(tasks);
                // выводим на экран отчет о количестве обработанных элементов
                Console.WriteLine("Обработанно элементов: {0}", itemCount);
                 Console.ReadLine();
        }
    }
}
 Результат выполнения программы использующую коллекцию ConcurrentStack

увеличить изображение
Рис. 15.4. Результат выполнения программы использующую коллекцию ConcurrentStack

ConcurrentBag

Коллекция СoncurrentBag представляет собой потокобезопасную неупорядоченную коллекцию объектов и реализует концепцию отображения потоков на используемые внутренне массивы, и старается избежать блокировок. Для доступа к элементам применяются методы Add(), TryPeek() и TryTake() (Табл. 15.5). СoncurrentBag можно представить как набор очередей с двусторонним доступом (deque). Каждый поток при работе с коллекцией обращается к своей собственной очереди, добавляя и удаляя элементы с ее начала. Когда случается так, что очередь одного потока пуста, а ему нужно извлечь элемент, то он извлекает его из очереди соседнего потока, но уже не сначала очереди, а с противоположного конца очереди. Такой подход позволяет практически не пересекаться разным потокам по данным.

Таблица 15.5. Основные методы коллекции ConcurrentBag
Имя Описание
Add(T) Добавляет объект в коллекцию.
TryPeek(out T) Пытается вернуть объект из коллекции без его удаления.
TryTake(out T) Пытается удалить и вернуть объект из коллекции

Пример использование коллекции СoncurrentBag представлен ниже:

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)
        {
            // создаем коллекцию
            ConcurrentBag<int> sharedBag = new ConcurrentBag<int>();
            // заполняем коллекцию в цикле с помощью метода Add
            for (int i = 0; i < 1000; i++)
            {
                sharedBag.Add(i);
            }
            // объявляем переменную-счетчик количества обработанных элементов
            int itemCount = 0;
            // создаем список задач
            Task[] tasks = new Task[10];
            for (int i = 0; i < tasks.Length; i++)
            {
                // создаем задачу
                tasks[i] = new Task(() =>
                {
                    while (sharedBag.Count > 0)
                    {
                        Thread.Sleep(10);
                        int queueElement;
                        // удаляем элемент из коллекции с помощью метода TryTake
                        bool gotElement = sharedBag.TryTake(out queueElement);
                        // увеливам значение переменной и сохраняем результат
                        if (gotElement)
                        {
                     
                      
                        Interlocked.Increment(ref itemCount);  } 
                    }
                });
                // запускаем новую задачу
                tasks[i].Start();
            }
                // ожидаем завершения всех задач
                Task.WaitAll(tasks);
                // выводим на экран отчет о количестве обработанных элементов
                Console.WriteLine("Обработанно элементов: {0}", itemCount);
                 Console.ReadLine();
    
        }
    }
}
 Результат выполнения программы использующую коллекцию СoncurrentBag

увеличить изображение
Рис. 15.5. Результат выполнения программы использующую коллекцию СoncurrentBag
Владимир Каширин
Владимир Каширин

Вопрос по Курсу: "Параллельное программирование с использованием MS VisualStudia 2010".

При компиляции Самостоятельного задания (одновременная отрисовка прямоугольников, эллипсов и выдача в текст-бокс случайного числа) среда предупреждает: suspend - устаревшая команда; примените monitor, mutex и т.п.

Создаётся впечатление, что Задание создано в более поздней среде, чем VS 2010.

Александр Гаврилов
Александр Гаврилов
Россия
Роман Дмитриев
Роман Дмитриев
Россия, Москва