Поделиться через


Параллельные контейнеры и объекты

Библиотека параллельных шаблонов (PPL) содержит несколько контейнеров и объектов, предоставляющих потокобезопасный доступ к элементам внутри них.

Параллельный контейнер предоставляет безопасный доступ к важнейшим операциям в режиме параллелизма.Функциональные возможности этих контейнеров аналогичны возможностям, предоставляемым библиотекой стандартных шаблонов (STL).Например concurrency::concurrent_vector похож на класс std::vector класса, за исключением, concurrent_vector класс позволяет добавлять элементы в параллельном режиме.Параллельные контейнеры рекомендуется использовать, если имеется параллельный код, требующий прав чтения и записи по отношению к одному и тому же контейнеру.

Параллельный объект совместно используется компонентами в режиме параллелизма.Процесс, вычисляющий состояние параллельного объекта в параллельном режиме, дает тот же результат, что и процесс, вычисляющий то же состояние последовательно.Concurrency::combinable класса является примером типа объекта одновременно.Класс combinable позволяет выполнять вычисления параллельно, а затем объединять результаты этих вычислений в общий результат.Параллельные объекты следует использовать, если бы иначе для синхронизации доступа к общей переменной или ресурсу необходимо было бы использовать механизм синхронизации, например мьютекс.

Подразделы

В этом разделе подробно описываются следующие параллельные контейнеры и объекты.

Параллельные контейнеры

  • Класс concurrent_vector

    • Различия между классами concurrent_vector и vector

    • Безопасные операции в режиме параллелизма

    • Безопасность исключений

  • Класс concurrent_queue

    • Различия между классами concurrent_queue и queue

    • Безопасные операции в режиме параллелизма

    • Поддержка итераторов

  • Класс concurrent_unordered_map

    • Различия между concurrent_unordered_map и unordered_map

    • Безопасные операции в режиме параллелизма

  • Класс concurrent_unordered_multimap

  • Класс concurrent_unordered_set

  • Класс concurrent_unordered_multiset

Параллельные объекты

  • Класс combinable

    • Методы и свойства

    • Примеры

Класс concurrent_vector

Concurrency::concurrent_vector класс является классом контейнер последовательности, так же, как std::vector класса, позволяет случайным образом получить доступ к его элементам.Класс concurrent_vector позволяет безопасно осуществлять операции добавления элементов и доступа к этим элементам в режиме параллелизма.Операции добавления не делают недействительными существующие указатели или итераторы.Операции доступа к итераторам и прохождения итераторов также являются безопасными в режиме параллелизма.

Dd504906.collapse_all(ru-ru,VS.110).gifРазличия между классами concurrent_vector и vector

Класс concurrent_vector во многом напоминает класс vector.Сложность операций добавления, доступа к элементам и итераторам для объекта concurrent_vector та же, что и для объекта vector.Далее показано, чем класс concurrent_vector отличается от vector.

  • Операции добавления, доступа к элементу или итератору и прохождения итератора в объекте concurrent_vector являются безопасными в режиме параллелизма.

  • Добавлять элементы можно только в конец объекта concurrent_vector.Класс concurrent_vector не предоставляет метод insert.

  • При выполнении присоединения объект concurrent_vector не использует семантику перемещения.

  • Класс concurrent_vector не предоставляет методы erase и pop_back.Как и с vector, для удаления всех элементов из объекта concurrent_vector следует использовать метод clear.

  • Класс concurrent_vector не хранит в памяти все элементы подряд.Поэтому класс concurrent_vector невозможно использовать всеми способами, которыми используется массив.Например, для переменной с именем v типа concurrent_vector выражение &v[0]+2 выдает неопределенное поведение.

  • Класс concurrent_vector определяет методы grow_by и grow_to_at_least.Эти методы аналогичны методу resize, но при этом их можно безопасно использовать в режиме параллелизма.

  • Объект concurrent_vector не изменяет расположение элементов при добавлении в объект новых элементов или изменении его размеров.Благодаря этому существующие указатели и итераторы остаются действительными при выполнении параллельных операций.

  • Среда выполнения не определяет специальную версию concurrent_vector для типа bool.

Dd504906.collapse_all(ru-ru,VS.110).gifБезопасные операции в режиме параллелизма

Все методы, добавляющие элементы в объект concurrent_vector, увеличивающие его размер или осуществляющие доступ к элементу в объекте concurrent_vector, являются безопасными в режиме параллелизма.Исключением из этого правила является метод resize.

В следующей таблице представлены наиболее распространенные методы и операторы класса concurrent_vector, являющиеся безопасными в режиме параллелизма.

at

end

operator[]

begin

front

push_back

back

grow_by

rbegin

capacity

grow_to_at_least

rend

empty

max_size

size

Операции, которые среда выполнения предоставляет для совместимости с STL, например, reserve, не являются безопасным параллелизма.В следующей таблице представлены наиболее распространенные методы и операторы, не являющиеся безопасными в режиме параллелизма.

assign

reserve

clear

resize

operator=

shrink_to_fit

Операции, изменяющие значение существующих элементов, не являются безопасными в режиме параллелизма.Для синхронизации параллельных операций чтения и записи в один и тот же элемент данных необходимо использовать объект синхронизации, такой как reader_writer_lock.Дополнительные сведения об объектах синхронизации см. в разделе Структуры данных синхронизации.

При преобразовании существующего кода, использующего vector, для использования concurrent_vector, параллельные операции могут стать причиной изменения поведения приложения.Рассмотрим следующую программу, параллельно выполняющую две задачи с объектом concurrent_vector.Первая задача добавляет элементы в объект concurrent_vector.Вторая задача вычисляет сумму всех элементов в этом объекте.

// parallel-vector-sum.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_vector.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // Create a concurrent_vector object that contains a few
   // initial elements.
   concurrent_vector<int> v;
   v.push_back(2);
   v.push_back(3);
   v.push_back(4);

   // Perform two tasks in parallel.
   // The first task appends additional elements to the concurrent_vector object.
   // The second task computes the sum of all elements in the same object.

   parallel_invoke(
      [&v] { 
         for(int i = 0; i < 10000; ++i)
         {
            v.push_back(i);
         }
      },
      [&v] {
         combinable<int> sums;
         for(auto i = begin(v); i != end(v); ++i) 
         {
            sums.local() += *i;
         }     
         wcout << L"sum = " << sums.combine(plus<int>()) << endl;
      }
   );
}

Хотя метод end является безопасным в режиме параллелизма, параллельный вызов метода push_back вызывает изменение значения, возвращаемого методом end.Число элементов, проходимых итератором, определить невозможно.Поэтому программа может выдавать разные результаты при каждом запуске.

Dd504906.collapse_all(ru-ru,VS.110).gifБезопасность исключений

Если при выполнении операции увеличения или назначения создается исключение, состояние объекта concurrent_vector становится недействительным.Поведение объекта concurrent_vector, находящегося в недействительном состоянии, является неопределенным, если не указано иное.Однако деструктор всегда освобождает память, выделяемую объектом, даже если объект находится в недействительном состоянии.

Тип данных элементов класса vector, _Ty, должен соответствовать следующим требованиям.В противном случае поведение класса concurrent_vector не определено.

  • Деструктор не должен создавать исключения.

  • Если конструктор по умолчанию или конструктор копии создает исключение, не следует использовать ключевое слово virtual для объявления деструктора, который, кроме того, должен правильно работать с инициализированной с нуля памятью.

Top

Класс concurrent_queue

Concurrency::concurrent_queue класса так же, как std::queue класса, позволяет доступ к его передней и задней элементов.Класс concurrent_queue позволяет безопасно выполнять операции постановки в очередь и удаления из очереди в режиме параллелизма.Класс concurrent_queue также обеспечивает поддержку итераторов, не являющихся безопасными в режиме параллелизма.

Dd504906.collapse_all(ru-ru,VS.110).gifРазличия между классами concurrent_queue и queue

Класс concurrent_queue во многом напоминает класс queue.Далее показано, чем класс concurrent_queue отличается от класса queue.

  • Операции постановки в очередь и удаления из очереди в объекте concurrent_queue являются безопасными в режиме параллелизма.

  • Класс concurrent_queue обеспечивает поддержку итераторов, не являющихся безопасными в режиме параллелизма.

  • Класс concurrent_queue не предоставляет методы front и pop.Класс concurrent_queue заменяет эти методы, определяя метод try_pop.

  • Класс concurrent_queue не предоставляет метод back.Следовательно, нельзя ссылаться на конец очереди.

  • Класс concurrent_queue предоставляет метод unsafe_size вместо метода size.Метод unsafe_size не является безопасным в режиме параллелизма.

Dd504906.collapse_all(ru-ru,VS.110).gifБезопасные операции в режиме параллелизма

Все методы, помещающие элементы в очередь или удаляющие их из очереди в объекте concurrent_queue, являются безопасными в режиме параллелизма.

В следующей таблице представлены наиболее распространенные методы и операторы класса concurrent_queue, являющиеся безопасными в режиме параллелизма.

empty

push

get_allocator

try_pop

Хотя метод empty является безопасным в режиме параллелизма, параллельная операция может стать причиной увеличения или уменьшения очереди до возвращения метода empty.

В следующей таблице представлены наиболее распространенные методы и операторы, не являющиеся безопасными в режиме параллелизма.

clear

unsafe_end

unsafe_begin

unsafe_size

Dd504906.collapse_all(ru-ru,VS.110).gifПоддержка итераторов

Класс concurrent_queue предоставляет итераторы, которые не являются безопасными в режиме параллелизма.Эти итераторы рекомендуется использовать только для отладки.

Итератор concurrent_queue проходит элементы только в прямом направлении.В следующей таблице показаны операторы, которые поддерживаются каждым итератором.

Оператор

Описание

operator++

Переходит к следующему элементу в очереди.Этот оператор перегружается, чтобы обеспечить семантику префиксного и постфиксного приращений.

operator*

Извлекает ссылку на текущий элемент.

operator->

Извлекает указатель на текущий элемент.

Top

Класс concurrent_unordered_map

Concurrency::concurrent_unordered_map класс является ассоциативный контейнера, так же, как std::unordered_map класс, управляющий разной длины последовательность элементов типа std::pair < const ключ, Ty >.Неупорядоченные карты можно рассматривать как словарь, который можно добавить пару ключ-значение для или искать значение по ключу.Этот класс полезен, когда имеется несколько потоков или задач, которые должны одновременно доступ к общим контейнера, вставьте в него или его обновления.

Следующий пример показывает базовую структуру для использования concurrent_unordered_map.В этом примере вставляет символ ключи в диапазоне [«», ' i'].Поскольку порядок операций не определено, конечное значение для каждого ключа также не определено.Тем не менее является безопасным для выполнения операций вставки в параллельном режиме.

// unordered-map-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_map.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the map in parallel.

    concurrent_unordered_map<char, int> map; 

    parallel_for(0, 1000, [&map](int i) {
        char key = 'a' + (i%9); // Geneate a key in the range [a,i].
        int value = i;          // Set the value to i.
        map.insert(make_pair(key, value));
    });

    // Print the elements in the map.
    for_each(begin(map), end(map), [](const pair<char, int>& pr) {
        wcout << L"[" << pr.first << L", " << pr.second << L"] ";
    });
}
/* Sample output:
    [e, 751] [i, 755] [a, 756] [c, 758] [g, 753] [f, 752] [b, 757] [d, 750] [h, 754]
*/

Пример, использующий concurrent_unordered_map карты и уменьшить операции параллельно, см. Практическое руководство. Параллельное выполнение операций сопоставления и сокращения числа элементов.

Dd504906.collapse_all(ru-ru,VS.110).gifРазличия между concurrent_unordered_map и unordered_map

Класс concurrent_unordered_map во многом напоминает класс unordered_map.Далее показано, чем класс concurrent_unordered_map отличается от unordered_map.

  • The erase, bucket, bucket_count, and bucket_size methods are named unsafe_erase, unsafe_bucket, unsafe_bucket_count, and unsafe_bucket_size, respectively.unsafe_ Соглашение об именах указывает, что эти методы не одновременности.Дополнительные сведения о безопасности параллельной обработки, см. Параллелизма строго типизированные операции.

  • Операций вставки не нарушают существующих указателей или итераторов, а также их изменить порядок элементов, которые уже существуют в области схемы.Вставка и обзор операции могут выполняться одновременно.

  • concurrent_unordered_mapподдерживает пересылку только итерации.

  • Вставка недействительными или не обновлять итераторы, возвращаемые equal_range.Конец диапазона вставки можно добавить несовпадение элементов.Начало итератором равно элемента.

Чтобы избежать взаимоблокировки, метод не concurrent_unordered_map удерживает блокировку вызывает распределитель памяти, функции хэширования или другой пользовательский код.Кроме того необходимо убедиться, хэш-функция всегда равно равно ключи совпадают.Наиболее хеш-функции равномерно распределять ключи по места код хэша.

Dd504906.collapse_all(ru-ru,VS.110).gifБезопасные операции в режиме параллелизма

concurrent_unordered_map Класс позволяет параллелизма строго типизированные операции вставки и доступа к элементу.Операции вставки недействительными существующие указатели или итераторов.Операции доступа к итераторам и прохождения итераторов также являются безопасными в режиме параллелизма.В следующей таблице перечислены часто используемые concurrent_unordered_map методов и операторов, которые являются одновременности.

at

count

find

key_eq

begin

empty

get_allocator

max_size

cbegin

end

hash_function

operator[]

cend

equal_range

Вставка

size

Хотя count безопасный метод может быть вызван из одновременно выполняющихся потоков, различные потоки могут получать разные результаты, если новое значение одновременно вставить в контейнер.

Ниже приведены наиболее часто используемых методов и операторов, которые не являются безопасным параллелизма.

clear

max_load_factor

rehash

load_factor

operator=

горячей замены

Помимо этих методов любого метода, начинается с unsafe_ также не является типизированным параллелизма.

Top

Класс concurrent_unordered_multimap

Concurrency::concurrent_unordered_multimap класс похож на concurrent_unordered_map класса, за исключением того, что позволяет несколько значений для сопоставления с тем же ключом.Он также отличается от concurrent_unordered_map следующим образом:

  • Concurrent_unordered_multimap::insert метод возвращает итератор вместо std::pair<iterator, bool>.

  • concurrent_unordered_multimap Класс не предоставляет operator[] , ни at метод.

Следующий пример показывает базовую структуру для использования concurrent_unordered_multimap.В этом примере вставляет символ ключи в диапазоне [«», ' i'].concurrent_unordered_multimapпозволяет иметь несколько значений ключа.

// unordered-multimap-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_map.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the map in parallel.

    concurrent_unordered_multimap<char, int> map; 

    parallel_for(0, 10, [&map](int i) {
        char key = 'a' + (i%9); // Geneate a key in the range [a,i].
        int value = i;          // Set the value to i.
        map.insert(make_pair(key, value));
    });

    // Print the elements in the map.
    for_each(begin(map), end(map), [](const pair<char, int>& pr) {
        wcout << L"[" << pr.first << L", " << pr.second << L"] ";
    });
}
/* Sample output:
    [e, 4] [i, 8] [a, 9] [a, 0] [c, 2] [g, 6] [f, 5] [b, 1] [d, 3] [h, 7]
*/

Top

Класс concurrent_unordered_set

Concurrency::concurrent_unordered_set класс похож на concurrent_unordered_map класса, за исключением того, что он управляет значений вместо пары "ключ-значение".concurrent_unordered_set Класс не предоставляет operator[] , ни at метод.

Следующий пример показывает базовую структуру для использования concurrent_unordered_set.В этом примере вставляет значения символов в диапазоне [«», ' i'].Безопасно для выполнения операций вставки в параллельном режиме.

// unordered-set-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_set.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the set in parallel.

    concurrent_unordered_set<char> set; 

    parallel_for(0, 10000, [&set](int i) {
        set.insert('a' + (i%9)); // Geneate a value in the range [a,i].
    });

    // Print the elements in the set.
    for_each(begin(set), end(set), [](char c) {
        wcout << L"[" << c << L"] ";
    });
}
/* Sample output:
    [e] [i] [a] [c] [g] [f] [b] [d] [h]
*/

Top

Класс concurrent_unordered_multiset

Concurrency::concurrent_unordered_multiset класс похож на concurrent_unordered_set класса, но разрешает повторяющиеся значения.Он также отличается от concurrent_unordered_set следующим образом:

  • Concurrent_unordered_multiset::insert метод возвращает итератор вместо std::pair<iterator, bool>.

  • concurrent_unordered_multiset Класс не предоставляет operator[] , ни at метод.

Следующий пример показывает базовую структуру для использования concurrent_unordered_multiset.В этом примере вставляет значения символов в диапазоне [«», ' i'].concurrent_unordered_multisetвключает значение возникает несколько раз.

// unordered-set-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_set.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the set in parallel.

    concurrent_unordered_multiset<char> set; 

    parallel_for(0, 40, [&set](int i) {
        set.insert('a' + (i%9)); // Geneate a value in the range [a,i].
    });

    // Print the elements in the set.
    for_each(begin(set), end(set), [](char c) {
        wcout << L"[" << c << L"] ";
    });
}
/* Sample output:
    [e] [e] [e] [e] [i] [i] [i] [i] [a] [a] [a] [a] [a] [c] [c] [c] [c] [c] [g] [g]
    [g] [g] [f] [f] [f] [f] [b] [b] [b] [b] [b] [d] [d] [d] [d] [d] [h] [h] [h] [h]
*/

Top

Класс combinable

Concurrency::combinable класс предоставляет хранилище для многократного использования, локальной памяти потока, позволяет выполнять вычисления детализированное и затем объединить эти вычисления конечного результата.Объект combinable можно сравнить с переменной сокращения.

Класс combinable полезно использовать, если имеется ресурс, совместно используемый в нескольких потоках или задачах.Класс combinable помогает исключить состояние с общим доступом, предоставляя доступ к общим ресурсам без блокировок.Следовательно, этот класс предоставляет альтернативу механизму синхронизации, например мьютексу, для синхронизации доступа к общим данным из различных потоков.

Dd504906.collapse_all(ru-ru,VS.110).gifМетоды и свойства

В следующей таблице показаны некоторые важные методы, принадлежащие к классу combinable.Дополнительные сведения о всех методах класса combinable см. в разделе Класс combinable.

Метод

Описание

local

Извлекает ссылку на локальную переменную, связанную с текущим контекстом потока.

clear

Удаляет все локальные переменные потока из объекта combinable.

combine

combine_each

Использует предоставленную функцию combine для создания окончательного значения из набора локальных вычислений потока.

Класс combinable является классом шаблона, который параметризуется по окончательному результату слияния.Если вызывается конструктор по умолчанию, тип параметров шаблона _Ty должен иметь конструктор по умолчанию и конструктор копии.Если тип параметров шаблона _Ty не имеет конструктора по умолчанию, необходимо вызвать версию перегрузки конструктора, принимающую в качестве параметра функцию инициализации.

После вызова метода combine или combine_each в объекте combinable можно хранить дополнительные данные.Кроме того, методы combine и combine_each можно вызывать несколько раз.Если в объекте combinable не меняется локальное значение, методы combine и combine_each дают при каждом вызове один и тот же результат.

Dd504906.collapse_all(ru-ru,VS.110).gifПримеры

Примеры использования класса combinable см. в следующих разделах.

Top

Связанные разделы

Ссылки

Класс concurrent_vector

Класс concurrent_queue

Класс concurrent_unordered_map

Класс concurrent_unordered_multimap

Класс concurrent_unordered_set

Класс concurrent_unordered_multiset

Класс combinable