Томский политехнический университет
Опубликован: 23.01.2013 | Доступ: свободный | Студентов: 1157 / 192 | Длительность: 12:09:00
Лекция 11:

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

ConcurrentDictionary

Коллекция ConcurrentDictionary представляет собой потокобезопасную коллекцию пар "ключ-значение", доступ к которой могут одновременно получать несколько потоков. Для доступа к элементам коллекции в не блокирующем режиме служат методы TryAdd(), TryGetValue(), TryRemove() и TryUpdate() (Табл. 15.6). Поскольку элементы основаны на ключах и значениях, ConcurrentDictionary<TKey, TVal> не реализует интерфейс IProducerConsumerCollection<T>.

Таблица 15.6. Основные методы коллекции ConcurrentDictionary
Имя Описание
TryAdd(TKey, TVal) Пытается добавить указанную пару "ключ-значение" в коллекцию.
TryGetValue(TKey, out TVal) Пытается получить значение, связанное с указанным ключом, из коллекции.
TryRemove(TKey, out TVal) Пытается удалить и вернуть значение с указанным ключом из коллекции.
TryUpdate(TKey, TVal, TVal) Сравнивает существующее значение указанного ключа с заданным значением и в случае их равенства обновляет ключ третьим значением.
ContainsKey(TKey) Определяет, содержится ли указанный ключ в словаре
GetOrAdd (TKey, TVal) Добавляет пару "ключ значение" в коллекцию, если ключ еще не существует.
AddOrUpdate (TKey, TVal, Func<TKey, TVal, TVal>) Добавляет пару "ключ значение" в коллекцию, если данный ключ еще не существует, или обновляет пару "ключ.

Методы, представленные в Табл. 15.6, следует использовать в следующих случаях:

  • Метод AddOrUpdate() используют, если необходимо добавить новое значение для заданного ключа, а также в том случае если ключ уже существует и необходимо заменить его значение;
  • Метод GetOrAdd() используют, если необходимо получить существующее значение для заданного ключа, а также в том случае если ключ не существует и задать значение паре "ключ-значение";
  • Методы TryAdd(), TryGetValue(), TryUpdate(), TryRemove() используют, если необходимо добавить, получить, обновить или удалить пару "ключ-значение", а также в том случае, если ключ уже существует или попытка завершилась по какой-либо причине ошибкой и необходимо выполнить альтернативные действия.

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace ConcurrentDictionaryExample
{
    class BankAccount
    {
        public int Balance
        {
            get;
            set;
        }
        class Program
        {
            static void Main(string[] args)
            {
                // создаем экземпляр банковского счета
                BankAccount account = new BankAccount();
                // создаем коллекцию
                ConcurrentDictionary<object, int> sharedDict
                    = new ConcurrentDictionary<object, int>();
                        // создаем список задач, которые возвращают целочисленный массив
                Task<int>[] tasks = new Task<int>[10];
                for (int i = 0; i < tasks.Length; i++)
                {
                    // помещаем начальные значения в словарь
                    sharedDict.TryAdd(i, account.Balance);
                    // создаем новую задачу
                    tasks[i] = new Task<int>((keyObj) =>
                    {
                        // создаем переменную для использования в цикле
                        int currentValue;
                        bool gotValue;
                        // создаем цикл для обновления баланса счета
                        for (int j = 0; j < 1000; j++)
                        {
                            // получаем текущее значение из словаря
                            gotValue = sharedDict.TryGetValue(keyObj, out currentValue);
                            // увеличиваем значение и обновляем словарь
                            sharedDict.TryUpdate(keyObj, currentValue + 1, currentValue);
                        }
                        // создаем переменную конечного результата
                        int result;
                        // получаем результат из словаря
                        gotValue = sharedDict.TryGetValue(keyObj, out result);
                        // возвращаем значение результата, если есть
                        if (gotValue)
                        {
                            return result;
                        }
                        else
                        {
                            // если нет результата - вызываем исключение
                            throw new Exception(
                                String.Format("Нет элементов данных доступных для объекта {0}", keyObj));
                        }
                    }, i);
                    // запускаем задачу
                    tasks[i].Start();
                }
                // обновляем баланс счета с помощью результатов выполнения задач
                for (int i = 0; i < tasks.Length; i++)
                {
                    account.Balance += tasks[i].Result;
                }
                // выводим значение счетчика
                Console.WriteLine("Ожидаемое значение: {0}, Баланс: {1}",
                    10000, account.Balance);
                Console.ReadLine();
            }
        }
    }
}
 Результат выполнения программы использующую коллекцию ConcurrentDictionary

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

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

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

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