Compartilhar via


Contêineres e objetos em paralelo

A biblioteca (PPL) de padrões de paralela inclui vários contêineres e objetos que fornecem acesso isento a seus elementos.

Um contêiner simultânea fornece acesso simultaneidade- seguro para operações as mais importantes. A funcionalidade desses contêineres se assemelha a aquelas que são fornecidas pela biblioteca padrão (STL) do modelo. Por exemplo, a classe de concurrency::concurrent_vector é semelhante à classe de std::vector , exceto que a classe de concurrent_vector permite anexar os elementos em paralelo. Use contêineres simultâneas enquanto você tiver código paralelo que requer tanto o acesso de leitura e gravação para o mesmo contêiner.

Um objeto simultâneo é compartilhado simultaneamente entre os componentes. Um processo que calcula o estado de um objeto simultânea em paralelo gerencia o mesmo resultado que outro processo que calcula o mesmo estado em série. A classe de concurrency::combinable é um exemplo de um tipo de objeto simultâneo. A classe de combinable permite executar computações em paralelo, e combina-a nas computações em um resultado final. Use objetos simultâneos quando você usaria de outra forma um mecanismo de sincronização, por exemplo, um mutex, para sincronizar o acesso a uma variável ou em um recurso compartilhado.

Seções

Este tópico descreve os seguintes objetos contêiner e paralelos em detalhes.

Contêiner simultâneas:

  • classe de concurrent_vector

    • Diferenças entre o concurrent_vector e o vetor

    • Operações Simultaneidade-seguras

    • Segurança de exceção

  • classe de concurrent_queue

    • Diferenças entre o concurrent_queue e a fila

    • Operações Simultaneidade-seguras

    • Suporte de iterador

  • classe de concurrent_unordered_map

    • Diferenças entre o concurrent_unordered_map e o unordered_map

    • Operações Simultaneidade-seguras

  • classe de concurrent_unordered_multimap

  • classe de concurrent_unordered_set

  • classe de concurrent_unordered_multiset

Objetos simultâneos:

  • classe combinável

    • Métodos e recursos

    • Exemplos

classe de concurrent_vector

A classe de concurrency::concurrent_vector é uma classe do contêiner de sequência que, assim como a classe de std::vector , permite aleatoriamente acessar seus elementos. A classe de concurrent_vector habilita simultaneidade- seguro acrescenta e operações de acesso do elemento. Acrescentar operações não invalidam os ponteiros existentes ou iteradores. As operações de acesso e de passagem do iterador também são simultaneidade- seguras.

Diferenças entre o concurrent_vector e o vetor

A classe de concurrent_vector assemelha-se à classe de vector . A complexidade do anexa, o acesso de elemento, e as operações de acesso de iterador em um objeto de concurrent_vector são as mesmas para um objeto de vector . Os seguintes pontos onde a seguir ilustram concurrent_vector difere de vector:

  • Anexar, o acesso de elemento, o acesso de iterador, e as operações de passagem do iterador em um objeto de concurrent_vector são simultaneidade- seguras.

  • Você pode adicionar os elementos somente ao final de um objeto de concurrent_vector . A classe de concurrent_vector não fornece o método de insert .

  • Um objeto de concurrent_vector não usa semântica de movimentação quando você anexa.

  • A classe de concurrent_vector não fornece métodos de erase ou de pop_back . Como com vector, use o método de espaço livre para remover todos os elementos de um objeto de concurrent_vector .

  • A classe de concurrent_vector não armazena seus elementos contiguamente na memória. Consequentemente, você não pode usar a classe de concurrent_vector de todas as maneiras que você pode usar uma matriz. Por exemplo, para uma variável denominada v do tipo concurrent_vector, a expressão &v[0]+2 gerencia comportamento indefinido.

  • A classe de concurrent_vector define os métodos de grow_by e de grow_to_at_least . Esses métodos lembram ao método de redimensionar , exceto que são simultaneidade- seguros.

  • Um objeto de concurrent_vector não realocar seus elementos quando você anexa ou o modo de redimensionamento. Isso habilita ponteiros existentes e iteradores permaneça válida durante operações simultâneas.

  • O tempo de execução não define uma versão especializada de concurrent_vector para o tipo bool.

Operações Simultaneidade-seguras

Todos os métodos que a acrescentam ou aumente o tamanho de um objeto de concurrent_vector , ou acessam um elemento em um objeto de concurrent_vector , são simultaneidade- seguros. A exceção a essa regra é o método de resize .

A tabela a seguir mostra os métodos e os operadores comuns de concurrent_vector que são simultaneidade- seguros.

em

end

operador[]

iniciar

frente

push_back

voltar

grow_by

rbegin

capacidade

grow_to_at_least

alugado

vazio

max_size

size

As operações que o tempo de execução fornece para compatibilidade com o STL, por exemplo, reserve, não são simultaneidade- seguras. A tabela a seguir mostra os métodos e os operadores comuns que não são seguras. simultaneidade-

atribuir

reservar

limpar

redimensionar

operador=

shrink_to_fit

Operações que alteram o valor dos elementos simultaneidade- existentes não são seguras. Use um objeto de sincronização como um objeto de reader_writer_lock para sincronizar gravação simultâneo e as operações de gravação no mesmo elemento de dados. Para obter mais informações sobre os objetos de sincronização, consulte Estruturas de dados de sincronização.

Quando você converte o código existente que usa vector para usar concurrent_vector, operações simultâneas podem provocar comportamento do aplicativo ser alterado. Por exemplo, considere o seguinte programa que executa duas tarefas simultaneamente em um objeto de concurrent_vector . A primeira tarefa acrescenta os elementos adicionais a um objeto de concurrent_vector . A segunda tarefa computa a soma de todos os elementos no mesmo objeto.

// 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;
      }
   );
}

Embora o método de end é simultaneidade- seguro, simultânea uma chamada ao método de push_back faz com que o valor retornado por end para alterar. O número de elementos que atravessa o iterador não é definido. Em virtude disso, esse programa pode gerar um resultado diferente a cada vez que você executá-lo.

Segurança de exceção

Se uma operação de crescimento ou atribuição gerou uma exceção, o estado do objeto de concurrent_vector torna-se inválido. O comportamento de um objeto de concurrent_vector que está em um estado inválido é indefinido salvo indicação em contrário. No entanto, o destruidor libera memória sempre que o objeto atribui, mesmo se o objeto está em um estado inválido.

O tipo de dados dos elementos de vetor, _Ty, deve atender aos seguintes requisitos. Caso contrário, o comportamento da classe de concurrent_vector é indefinido.

  • O destruidor não deve lançar.

  • Se lançará do construtor da opção ou de impressão, o destruidor não devem ser declarados usando a palavra-chave de virtual e deve funcionar corretamente com memória zero inicializada.

[Superior]

classe de concurrent_queue

A classe de concurrency::concurrent_queue , assim como a classe de std::queue , o permite acessar os elementos da frente e do backup. A classe de concurrent_queue habilita operações simultaneidade- seguras de enfileiramento e de retirada da fila. A classe de concurrent_queue também fornece suporte de iterador que não é simultaneidade- seguro.

Diferenças entre o concurrent_queue e a fila

A classe de concurrent_queue assemelha-se à classe de queue . Os seguintes pontos onde a seguir ilustram concurrent_queue difere de queue:

  • As operações de enfileiramento e de retirada de fila em um objeto de concurrent_queue são simultaneidade- seguras.

  • A classe de concurrent_queue fornece suporte de iterador que não é simultaneidade- seguro.

  • A classe de concurrent_queue não fornece métodos de front ou de pop . A classe de concurrent_queue substituirá esses métodos definindo o método de try_pop .

  • A classe de concurrent_queue não fornece o método de back . Consequentemente, você não pode fazer referência ao final da fila.

  • A classe de concurrent_queue fornece o método de unsafe_size em vez do método de size . O método de unsafe_size não é simultaneidade-seguro.

Operações Simultaneidade-seguras

Todos os métodos que a linha ou a remover da fila de um objeto de concurrent_queue são simultaneidade- seguros.

A tabela a seguir mostra os métodos e os operadores comuns de concurrent_queue que são simultaneidade- seguros.

vazio

envio

get_allocator

try_pop

Embora o método de empty é simultaneidade- seguro, uma operação simultânea pode fazer com que a fila seja ampliada ou reduzida antes do método de empty retorna.

A tabela a seguir mostra os métodos e os operadores comuns que não são seguras. simultaneidade-

limpar

unsafe_end

unsafe_begin

unsafe_size

Suporte de iterador

concurrent_queue fornece os iteradores que não são seguras. simultaneidade- Recomendamos que você use esses iteradores depurando somente.

Um iterador de concurrent_queue referem aos elementos na direção somente encaminhada. A tabela a seguir mostra os operadores que da suporte a cada iterador.

Operador

Descrição

operator++

Adiantamentos para o próximo item na fila. Esse operador é sobrecarregado para fornecer o incremento previamente e a semântica de pós-atualização incremento.

operator*

Recupera uma referência ao item atual.

operator->

Recupera um ponteiro para o item atual.

[Superior]

classe de concurrent_unordered_map

A classe é uma classe de concurrency::concurrent_unordered_map associativa de contêiner que, assim como a classe de std::unordered_map controle, uma sequência de variar- comprimento dos elementos do tipo std::pair<chave de const, Ty>. Pense de um mapa não ordenada como um dicionário que você pode adicionar um par de chave e o valor para ou pesquisar um valor pela chave. Esta classe é útil quando você tem vários threads ou tarefas que têm que simultaneamente acessar um contêiner compartilhado, o insere nela, ou a ser atualizada.

O exemplo a seguir mostra a estrutura básica para usar concurrent_unordered_map. Este exemplo insere chaves de caractere no intervalo [“a”, “i”]. Como a ordem das operações é indefinido, o valor final de cada chave também é indefinido. No entanto, é seguro ser executados em paralelo as inserções.

// 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]
*/

Para obter um exemplo que usa concurrent_unordered_map para executar um mapa e reduzir em paralelo a operação, consulte Como realizar operações de mapa e redução em paralelo.

Diferenças entre o concurrent_unordered_map e o unordered_map

A classe de concurrent_unordered_map assemelha-se à classe de unordered_map . Os seguintes pontos onde a seguir ilustram concurrent_unordered_map difere de unordered_map:

  • erase, bucket, bucket_count, e os métodos de bucket_size são nomeados unsafe_erase, unsafe_bucket, unsafe_bucket_count, e unsafe_bucket_size, respectivamente. A convenção de nomenclatura de unsafe_ indica que esses métodos não se simultaneidade- seguros. Para obter mais informações sobre a segurança de simultaneidade, consulte Operações Simultaneidade-seguras.

  • As operações de inserção não invalidam os ponteiros existentes ou iteradores, ou para alterar a ordem dos itens já existentes no mapa. A inserção e operações cruzadas podem ocorrer simultaneamente.

  • o oferece suporte à iteraçãoconcurrent_unordered_map de encaminhamento somente.

  • A inserção não invalida nem atualiza os iteradores que são retornados por equal_range. A inserção possível adicionar itens diferentes ao final do intervalo. Os pontos de iterador inicial igual a um item.

Para ajudar a evitar o deadlock, nenhum método de propriedades de concurrent_unordered_map um bloqueio ao chamar o alocador de memória, funções hash, ou outro código definido pelo usuário. Além disso, você deve assegurar que a função de hash é avaliada sempre chaves iguais ao mesmo valor. As melhores funções hash distribuição de chaves uniformemente do espaço de código hash.

Operações Simultaneidade-seguras

A classe de concurrent_unordered_map habilita operações simultaneidade- seguras de inserção e de elemento acesso. As operações de inserção não invalidam os ponteiros existentes ou iteradores. As operações de acesso e de passagem do iterador também são simultaneidade- seguras. A tabela a seguir mostra os métodos e operadores de uso geral de concurrent_unordered_map que são simultaneidade- seguros.

em

count

find

key_eq

begin

empty

get_allocator

max_size

cbegin

end

hash_function

operador[]

cend

equal_range

inserir

size

Embora o método de count pode ser chamado com segurança simultaneamente de executar threads, os threads diferentes podem receber resultados diferentes se um novo valor é inserido simultaneamente no contêiner.

A tabela a seguir mostra os métodos e operadores de uso geral que não são seguras. simultaneidade-

clear

max_load_factor

rehash

load_factor

operador=

troca

Além desses métodos, nenhum método que iniciar com unsafe_ também não é simultaneidade- seguro.

[Superior]

classe de concurrent_unordered_multimap

A classe de concurrency::concurrent_unordered_multimap assemelha-se à classe de concurrent_unordered_map exceto que permite vários valores mapear para a mesma chave. Também é diferente de concurrent_unordered_map das seguintes maneiras:

  • O método de concurrent_unordered_multimap::insert retorna um iterador em vez de std::pair<iterator, bool>.

  • A classe de concurrent_unordered_multimap não fornece operator[] ou o método de at .

O exemplo a seguir mostra a estrutura básica para usar concurrent_unordered_multimap. Este exemplo insere chaves de caractere no intervalo [“a”, “i”]. concurrent_unordered_multimap habilita uma chave para ter vários valores.

// 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]
*/

[Superior]

classe de concurrent_unordered_set

A classe de concurrency::concurrent_unordered_set assemelha-se à classe de concurrent_unordered_map exceto que gerencia valores em vez de pares de chave e o valor. A classe de concurrent_unordered_set não fornece operator[] ou o método de at .

O exemplo a seguir mostra a estrutura básica para usar concurrent_unordered_set. Este exemplo insere valores de caractere no intervalo [“a”, “i”]. É seguro ser executados em paralelo as inserções.

// 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]
*/

[Superior]

classe de concurrent_unordered_multiset

A classe de concurrency::concurrent_unordered_multiset assemelha-se à classe de concurrent_unordered_set exceto que permite valores duplicados. Também é diferente de concurrent_unordered_set das seguintes maneiras:

  • O método de concurrent_unordered_multiset::insert retorna um iterador em vez de std::pair<iterator, bool>.

  • A classe de concurrent_unordered_multiset não fornece operator[] ou o método de at .

O exemplo a seguir mostra a estrutura básica para usar concurrent_unordered_multiset. Este exemplo insere valores de caractere no intervalo [“a”, “i”]. concurrent_unordered_multiset habilita um valor ocorra várias vezes.

// 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]
*/

[Superior]

classe combinável

A classe de concurrency::combinable fornece reutilizável, o armazenamento de thread local que permite executar computações refinados e mesclar essas computações em um resultado final. Você pode pensar em um objeto de combinable como uma variável de redução.

A classe de combinable é útil quando você tem um recurso que é compartilhado entre vários threads ou tarefas. Ajuda da classe de combinable você elimina o estado compartilhado fornecendo acesso a recursos compartilhados em um modo lock-partition-id&gt livre. Em virtude disso, esta classe fornece uma alternativa para usar um mecanismo de sincronização, por exemplo, um mutex, para sincronizar o acesso a dados compartilhados de vários threads.

Métodos e recursos

A tabela a seguir mostra alguns dos métodos importantes da classe de combinable . Para obter mais informações sobre todos os métodos da classe de combinable , consulte Classe combinable.

Método

Descrição

local

Recupera uma referência à variável local associado ao contexto do thread atual.

limpar

Remove todas as variáveis de thread local do objeto de combinable .

combinar

combine_each

Usa a função fornecida de combinar para gerar um valor final do conjunto de todas as computações de thread local.

A classe de combinable é uma classe do modelo com parâmetros no resultado final mesclada. Se você chamar o construtor padrão, o tipo de parâmetro do modelo de _Ty deve ter um construtor padrão e um construtor de cópia. Se o tipo de parâmetro do modelo de _Ty não tem um construtor padrão, chame a versão do construtor sobrecarregada que usa uma função de inicialização como o parâmetro.

Você pode armazenar dados adicionais em um objeto de combinable depois que você chama os métodos de combinar ou de combine_each . Você também pode chamar os métodos de combine e de combine_each várias vezes. Se nenhum valor local em um objeto de combinable muda, os métodos de combine e de combine_each gerenciem o mesmo resultado toda vez que são chamados.

Exemplos

Para obter exemplos sobre como usar a classe de combinable , consulte os seguintes tópicos:

[Superior]

Tópicos relacionados

Referência

Classe concurrent_vector

Classe concurrent_queue

Classe concurrent_unordered_map

Classe concurrent_unordered_multimap

Classe concurrent_unordered_set

Classe concurrent_unordered_multiset

Classe combinable