Blocos de mensagens assíncronas

A biblioteca de agentes fornece vários tipos de bloco de mensagens que permitem que você propagar as mensagens entre os componentes do aplicativo de maneira segura para thread. Esses tipos de bloco de mensagens geralmente são usados com diversas rotinas de transmissão de mensagens, como Concurrency::send, Concurrency::asend, Concurrency::receive, e Concurrency::try_receive. Para obter mais informações sobre as rotinas que são definidas pela biblioteca de agentes de transmissão de mensagens, consulte Funções de transmissão de mensagens.

Seções

Este tópico contém as seções a seguir:

  • Origens e destinos

  • Propagação de mensagem

  • Visão geral dos tipos de bloco de mensagem

  • Classe de unbounded_buffer

  • overwrite_buffer classe

  • single_assignment classe

  • chamada de classe

  • Transformador de classe

  • Classe de opção

  • Classes de associação e multitype_join

  • Timer de classe

  • Filtragem de mensagens

  • Reserva de mensagem

Origens e destinos

Origens e destinos são dois participantes importantes em passagem de mensagens. A fonte se refere a um ponto de extremidade de comunicação que envia mensagens. A destino se refere a um ponto de extremidade de comunicação que recebe mensagens. Você pode pensar em uma fonte de um ponto de extremidade que você ler e um destino como um ponto de extremidade que você grava. Aplicativos se conectam a origens e destinos juntos para o formulário redes de mensagens.

A biblioteca de agentes usa duas classes abstratas para representar as origens e destinos: Concurrency::ISource e Concurrency::ITarget. Esse ato de tipos de bloco de mensagens como fontes derivam de ISource; Esse ato de tipos de bloco de mensagens como destinos derivam de ITarget. Esse ato de tipos de bloco de mensagens como origens e destinos derivam ambos ISource e ITarget.

go to top

Propagação de mensagem

Propagação de mensagem é o ato de enviar uma mensagem de um componente para outro. Quando um bloco de mensagens é oferecido uma mensagem, ele pode aceitar, recusar ou adiar essa mensagem. Cada tipo de bloco de mensagem, armazena e transmite mensagens de maneiras diferentes. Por exemplo, o unbounded_buffer classe armazena um número ilimitado de mensagens, o overwrite_buffer classe armazena uma única mensagem por vez, e a classe de transformador armazena uma versão alterada de cada mensagem. Esses tipos de bloco de mensagem são descritos mais detalhadamente mais adiante neste documento.

Quando um bloco de mensagens aceita uma mensagem, ele pode, opcionalmente, realizar o trabalho e, o bloco de mensagem é uma fonte para passar a mensagem resultante para outro membro da rede. Um bloco de mensagens pode usar uma função de filtro para rejeitar mensagens que não deseja receber. Filtros são descritos mais detalhadamente mais adiante neste tópico, na seção Filtragem de mensagens. Um bloco de mensagens que adia a uma mensagem pode reservar a mensagem e consumi-los posteriormente. Reserva de mensagem é descrita em mais detalhes posteriormente neste tópico, na seção Mensagem reserva.

A biblioteca de agentes permite blocos de mensagem para assíncrona ou síncrona passar mensagens. Quando você passa uma mensagem para um bloco de mensagens de forma síncrona, por exemplo, usando o send função, o runtime bloqueia o contexto atual até que o bloco de destino aceita ou rejeita a mensagem. Quando você passa uma mensagem para um bloco de mensagens de forma assíncrona, por exemplo, usando o asend função, o runtime oferece a mensagem para o destino, e se o destino aceita a mensagem, o runtime agenda uma tarefa assíncrona que propaga a mensagem para o receptor. O runtime usa tarefas leves para propagar as mensagens de forma cooperativa. Para obter mais informações sobre tarefas leves, consulte Agendador de tarefas (Runtime de simultaneidade).

Aplicativos conectam origens e destinos para redes de mensagens do formulário. Normalmente, você vincula a rede e a chamada send ou asend para passar dados da rede. Para se conectar a um bloco de mensagem de origem para um destino, chame o Concurrency::ISource::link_target método. Para desconectar um bloco de origem de um destino, chame o Concurrency::ISource::unlink_target método. Para desconectar-se um bloco de origem de todos os seus destinos, chame o Concurrency::ISource::unlink_targets método. Quando um dos tipos de bloco de mensagens predefinidas sai do escopo ou é destruído, ele automaticamente desconecta próprio de qualquer bloco de destino. Alguns tipos de bloco de mensagem restringem o número máximo de destinos podem gravar em. A seção a seguir descreve as restrições que se aplicam a tipos de bloco de mensagens predefinidas.

go to top

Visão geral dos tipos de bloco de mensagem

A tabela a seguir descreve brevemente a função dos tipos de bloco de mensagens importantes.

  • unbounded_buffer
    Armazena uma fila de mensagens.

  • overwrite_buffer
    Armazena uma mensagem que pode ser gravada e lidos a partir de várias vezes.

  • single_assignment
    Armazena uma mensagem que pode ser gravada uma vez e ler várias vezes.

  • chamada
    Realiza o trabalho quando ele recebe uma mensagem.

  • Transformador
    Realiza o trabalho quando ele recebe os dados e envia o resultado desse trabalho para outro bloco de destino. O transformer classe pode atuar em diferente entrada e saída de tipos.

  • Escolha
    Seleciona a primeira mensagem disponível a partir de um conjunto de fontes.

  • associação e associação de multitype
    Aguarde até que todas as mensagens para ser recebido de um conjunto de fontes e combine as mensagens em uma única mensagem para outro bloco de mensagem.

  • Timer
    Envia uma mensagem para um bloco de destino em um intervalo regular.

Esses tipos de bloco de mensagens têm características diferentes que os tornam útil para situações diferentes. Estas são algumas das características:

  • Tipo de propagação: Se o bloco de mensagem atua como uma fonte de dados, um receptor de dados ou ambos.

  • Pedidos de mensagem: Se o bloco de mensagens mantém a ordem original, as mensagens são enviadas ou recebidas. Cada tipo de bloco de mensagens predefinidas mantém a ordem original em que envia ou recebe mensagens.

  • Contagem de origem: O número máximo de fontes que o bloco de mensagens pode ler.

  • Contagem de destino: O número máximo de destinos que o bloco de mensagens pode gravar.

A tabela a seguir mostra como essas características referem-se a vários tipos de bloco de mensagens.

Tipo de bloco de mensagem

Tipo de propagação (origem, destino ou ambos)

Mensagem de pedidos (encomendado ou Unordered)

Contagem de origem

Contagem de destino

unbounded_buffer

Ambos

Ordenados

Não vinculado

Não vinculado

overwrite_buffer

Ambos

Ordenados

Não vinculado

Não vinculado

single_assignment

Ambos

Ordenados

Não vinculado

Não vinculado

call

Destino

Ordenados

Não vinculado

Não aplicável

transformer

Ambos

Ordenados

Não vinculado

1

choice

Ambos

Ordenados

10

1

join

Ambos

Ordenados

Não vinculado

1

multitype_join

Ambos

Ordenados

10

1

timer

Origem

Não aplicável

Não aplicável

1

As seções a seguir descrevem os tipos de bloco de mensagens mais detalhadamente.

go to top

Classe de unbounded_buffer

O Concurrency::unbounded_buffer classe representa uma estrutura geral de mensagens assíncrona. Essa classe armazena o primeiro na fila primeira a sair (FIFO) das mensagens que pode ser gravado por várias fontes ou ler por vários destinos. Quando um destino recebe uma mensagem de um unbounded_buffer o objeto, essa mensagem é removida da fila de mensagem. Portanto, embora um unbounded_buffer objeto pode ter vários destinos, apenas um destino receberá cada mensagem. O unbounded_buffer classe é útil quando você deseja passar várias mensagens para outro componente, e esse componente deve receber cada mensagem.

Exemplo

O exemplo a seguir mostra a estrutura básica de como trabalhar com o unbounded_buffer classe. Este exemplo envia três valores para um unbounded_buffer de objeto e, em seguida, lê os valores do mesmo objeto.

// unbounded_buffer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an unbounded_buffer object that works with
   // int data.
   unbounded_buffer<int> items;

   // Send a few items to the unbounded_buffer object.
   send(items, 33);
   send(items, 44);
   send(items, 55);

   // Read the items from the unbounded_buffer object and print
   // them to the console.
   wcout << receive(items) << endl;
   wcout << receive(items) << endl;
   wcout << receive(items) << endl;
}

Esse exemplo produz a seguinte saída.

33
44
55

Para obter um exemplo completo que mostra como usar o unbounded_buffer da classe, consulte Como: Implementar vários padrões de produtor de consumidor.

go to top

overwrite_buffer classe

O Concurrency::overwrite_buffer classe semelhante a unbounded_buffer de classe, exceto que um overwrite_buffer objeto armazena apenas uma mensagem. Além disso, quando um destino recebe uma mensagem de um overwrite_buffer o objeto, a mensagem não é removida do buffer. Portanto, vários destinos recebem uma cópia da mensagem.

O overwrite_buffer classe é útil quando você deseja passar várias mensagens para outro componente, mas esse componente precisa apenas o mais recente valor. Essa classe também é útil quando você deseja transmitir uma mensagem a vários componentes.

Exemplo

O exemplo a seguir mostra a estrutura básica de como trabalhar com o overwrite_buffer classe. Este exemplo envia três valores para um overwrite _buffer objeto e, em seguida, as leituras que o valor atual ao mesmo objeto três vezes. Este exemplo é semelhante ao exemplo para o unbounded_buffer classe. No entanto, o overwrite_buffer classe armazena apenas uma mensagem. Além disso, o runtime não remove a mensagem a partir de um overwrite_buffer o objeto após a sua leitura.

// overwrite_buffer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an overwrite_buffer object that works with
   // int data.
   overwrite_buffer<int> item;

   // Send a few items to the overwrite_buffer object.
   send(item, 33);
   send(item, 44);
   send(item, 55);

   // Read the current item from the overwrite_buffer object and print
   // it to the console three times.
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
}

Esse exemplo produz a seguinte saída.

55
55
55

Para obter um exemplo completo que mostra como usar o overwrite_buffer da classe, consulte Como: Implementar vários padrões de produtor de consumidor.

go to top

single_assignment classe

O Concurrency::single_assignment classe semelhante a overwrite_buffer de classe, exceto que um single_assignment objeto pode ser gravado em apenas uma vez. Como o overwrite_buffer classe, quando um destino recebe uma mensagem de um single_assignment o objeto, a mensagem não é removida do objeto. Portanto, vários destinos recebem uma cópia da mensagem. O single_assignment classe é útil quando você deseja transmitir uma mensagem para vários componentes.

Exemplo

O exemplo a seguir mostra a estrutura básica de como trabalhar com o single_assignment classe. Este exemplo envia três valores para um single_assignment objeto e, em seguida, as leituras que o valor atual ao mesmo objeto três vezes. Este exemplo é semelhante ao exemplo para o overwrite_buffer classe. Embora tanto o overwrite_buffer e single_assignment classes armazena uma única mensagem, o single_assignment classe pode ser gravado em apenas uma vez.

// single_assignment-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an single_assignment object that works with
   // int data.
   single_assignment<int> item;

   // Send a few items to the single_assignment object.
   send(item, 33);
   send(item, 44);
   send(item, 55);

   // Read the current item from the single_assignment object and print
   // it to the console three times.
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
}

Esse exemplo produz a seguinte saída.

33
33
33

Para obter um exemplo completo que mostra como usar o single_assignment da classe, consulte Demonstra Passo a passo: A implementação de futuros.

go to top

chamada de classe

O Concurrency::call classe atua como um receptor de mensagem que executa uma função de trabalho quando ele recebe dados. Essa função de trabalho pode ser uma expressão lambda, um objeto de função ou um ponteiro de função. A call objeto se comporta diferente uma chamada de função comum porque ele atua em paralelo a outros componentes que enviam mensagens para o proprietário. Se um call objeto está executando o trabalho quando ele recebe uma mensagem, ele adiciona a mensagem para uma fila. Cada call processos de objeto na fila de mensagens na ordem em que são recebidos.

Exemplo

O exemplo a seguir mostra a estrutura básica de como trabalhar com o call classe. Este exemplo cria um call objeto que imprime cada valor que ele recebe ao console. O exemplo, em seguida, envia os três valores para o call objeto. Porque o call objeto processa mensagens em um segmento separado, este exemplo também usa uma variável de contador e um evento objeto para garantir que o call objeto processa todas as mensagens antes de wmain retorna a função.

// call-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // An event that is set when the call object receives all values.
   event received_all;

   // Counts the 
   long receive_count = 0L;
   long max_receive_count = 3L;

   // Create an call object that works with int data.
   call<int> target([&received_all,&receive_count,max_receive_count](int n) {
      // Print the value that the call object receives to the console.
      wcout << n << endl;

      // Set the event when all messages have been processed.
      if (++receive_count == max_receive_count)
         received_all.set();
   });

   // Send a few items to the call object.
   send(target, 33);
   send(target, 44);
   send(target, 55);

   // Wait for the call object to process all items.
   received_all.wait();
}

Esse exemplo produz a seguinte saída.

33
44
55

Para obter um exemplo completo que mostra como usar o call da classe, consulte Como: Fornecer funções de trabalho para as Classes de chamada e o transformador.

go to top

Transformador de classe

O Concurrency::transformer classe atua como um receptor de mensagem e o remetente da mensagem. O transformer classe semelhante a call classe porque ele executa uma função de trabalho definidos pelo usuário quando ele recebe dados. No entanto, o transformer classe também envia o resultado da função de trabalho para objetos de destinatário. Como um call o objeto, um transformer objeto atua em paralelo a outros componentes que enviam mensagens para o proprietário. Se um transformer objeto está executando o trabalho quando ele recebe uma mensagem, ele adiciona a mensagem para uma fila. Cada transformer objeto processar suas mensagens enfileiradas na ordem em que são recebidos.

O transformer classe envia sua mensagem para um destino. Se você definir a _PTarget parâmetro no construtor para NULL, mais tarde você pode especificar o destino chamando o Concurrency::link_target método.

Ao contrário de todos os outros tipos de bloco de mensagens assíncronas são fornecidos pela biblioteca de agentes, a transformer classe pode atuar em diferente entrada e saída de tipos. Essa capacidade de transformar dados de um tipo para outro faz a transformer classe um componente-chave em muitas redes simultâneas. Além disso, você pode adicionar mais funcionalidade de paralela refinada na função de trabalho de um transformer objeto.

Exemplo

O exemplo a seguir mostra a estrutura básica de como trabalhar com o transformer classe. Este exemplo cria um transformer objeto múltiplos cada entrados int valor de 0,33 para produzir um double o valor como saída. O exemplo, em seguida, recebe os valores transformados da mesma transformer de objeto e as imprime no console.

// transformer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an transformer object that receives int data and 
   // sends double data.
   transformer<int, double> third([](int n) {
      // Return one-third of the input value.
      return n * 0.33;
   });

   // Send a few items to the transformer object.
   send(third, 33);
   send(third, 44);
   send(third, 55);

   // Read the processed items from the transformer object and print
   // them to the console.
   wcout << receive(third) << endl;
   wcout << receive(third) << endl;
   wcout << receive(third) << endl;
}

Esse exemplo produz a seguinte saída.

10.89
14.52
18.15

Para obter um exemplo completo que mostra como usar o transformer da classe, consulte Como: Transformador de uso em um Pipeline de dados.

go to top

Classe de opção

O Concurrency::choice classe seleciona a primeira mensagem disponível a partir de um conjunto de fontes. O choice classe representa um mecanismo de fluxo de controle, em vez de um mecanismo de fluxo de dados (o tópico Biblioteca de agentes assíncronos descreve as diferenças entre o fluxo de dados e o fluxo de controle).

Ler a partir de um objeto de opção é semelhante ao chamar a função de API do Windows WaitForMultipleObjects quando tem a bWaitAll parâmetro definido como FALSE. No entanto, o choice classe vincula os dados para o evento propriamente dito, em vez da um objeto de sincronização externa.

Normalmente, você usa o choice classe junto com o Concurrency::receive a função de fluxo de controle de unidade em seu aplicativo. Use o choice classe quando você precisa selecionar entre os buffers de mensagem que possuem tipos diferentes. Use o single_assignment classe quando você precisa selecionar entre os buffers de mensagem que tem o mesmo tipo.

A ordem na qual você vincular fontes para uma choice objeto é importante porque ele pode determinar qual mensagem está selecionada. Por exemplo, considere o caso onde você vincular vários buffers de mensagem que já contêm uma mensagem para um choice objeto. O choice objeto seleciona a mensagem da primeira fonte vinculado a. Depois de vincular todas as fontes, o choice objeto preserva a ordem em que cada fonte recebe uma mensagem.

Exemplo

O exemplo a seguir mostra a estrutura básica de como trabalhar com o choice classe. Este exemplo usa a Concurrency::make_choice função para criar um choice objeto que seleciona entre as três blocos de mensagem. O exemplo calcula vários números de Fibonacci e armazena cada resultado em um bloco de mensagem diferente. O exemplo, em seguida, imprime ao console uma mensagem que baseia-se a operação que foi concluída pela primeira vez.

// choice-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
   if (n < 2)
      return n;
   return fibonacci(n-1) + fibonacci(n-2);
}

int wmain()
{
   // Although the following thee message blocks are written to one time only, 
   // this example illustrates the fact that the choice class works with 
   // different message block types.

   // Holds the 35th Fibonacci number.
   single_assignment<int> fib35;
   // Holds the 37th Fibonacci number.
   overwrite_buffer<int> fib37;
   // Holds half of the 42nd Fibonacci number.
   unbounded_buffer<double> half_of_fib42;   

   // Create a choice object that selects the first single_assignment 
   // object that receives a value.
   auto select_one = make_choice(&fib35, &fib37, &half_of_fib42);

   // Execute a few lengthy operations in parallel. Each operation sends its 
   // result to one of the single_assignment objects.
   parallel_invoke(
      [&fib35] { send(fib35, fibonacci(35)); },
      [&fib37] { send(fib37, fibonacci(37)); },
      [&half_of_fib42] { send(half_of_fib42, fibonacci(42) * 0.5); }
   );

   // Print a message that is based on the operation that finished first.
   switch (receive(select_one))
   {
   case 0:
      wcout << L"fib35 received its value first. Result = " 
            << receive(fib35) << endl;
      break;
   case 1:
      wcout << L"fib37 received its value first. Result = " 
            << receive(fib37) << endl;
      break;
   case 2:
      wcout << L"half_of_fib42 received its value first. Result = " 
            << receive(half_of_fib42) << endl;
      break;
   default:
      wcout << L"Unexpected." << endl;
      break;
   }
}

Este exemplo produz a saída de exemplo a seguir:

fib35 received its value first. Result = 9227465

Porque a tarefa que computa a 35th o número de Fibonacci não é garantido para concluir o primeiro, a saída deste exemplo pode variar.

Este exemplo usa a Concurrency::parallel_invoke o algoritmo para calcular os números de Fibonacci em paralelo. Para obter mais informações sobre o parallel_invoke, consulte Algoritmos paralelos.

Para obter um exemplo completo que mostra como usar o choice da classe, consulte Como: Selecione entre as tarefas concluídas.

go to top

Classes de associação e multitype_join

O Concurrency::join e Concurrency::multitype_join classes permitem que você espera para cada membro de um conjunto de fontes receber uma mensagem. O join classe atua em objetos de origem que possuem um tipo de mensagem comum. O multitype_join classe atua em objetos de origem que podem ter tipos de mensagem diferente.

Leitura de um join ou multitype_join objeto semelhante ao chamar a função de API do Windows WaitForMultipleObjects quando tem a bWaitAll parâmetro definido como TRUE. No entanto, apenas como um choice o objeto, join e multitype_join objetos usam um mecanismo de evento que vincula os dados para o evento propriamente dito, em vez da um objeto de sincronização externa.

Leitura de um join objeto produz uma std:: Vector objeto. Leitura de um multitype_join objeto produz uma std::tuple objeto. Elementos são exibidos nesses objetos na mesma ordem seus buffers de origem correspondentes estão vinculadas a join ou multitype_join objeto. Porque a ordem na qual você vincula a fonte de buffers para um join ou multitype_join objeto é associado com a ordem dos elementos no resultante vector ou tuple o objeto, recomendamos que você não desvincular um buffer de origem existente da associação. Isso pode resultar em comportamento não especificado.

Ingressa greedy Versus não Greedy.

O join e multitype_join classes suportam o conceito de associações greedy e não greedy. A associação greedy aceita uma mensagem de cada uma das suas origens, como as mensagens ficam disponíveis até que todas as mensagens estão disponíveis. A - greedy associação recebe mensagens em duas fases. Primeiro, uma associação não greedy aguarda até que ela é oferecida uma mensagem de cada uma das suas fontes. Em segundo lugar, depois que todas as mensagens de origem estão disponíveis, uma associação não greedy tenta reservar cada uma dessas mensagens. Se ele pode reservar a cada mensagem, ele consome todas as mensagens e propaga a seu destino. Caso contrário, ele libera, ou cancela, as reservas de mensagem e aguarda novamente a cada fonte receber uma mensagem.

Associações greedy executam melhor a associações não greedy porque eles aceitam mensagens imediatamente. No entanto, em casos raros, junções greedy podem levar a deadlocks. Use uma associação não greedy, quando você tem várias associações que contêm um ou mais objetos de origem compartilhada.

Exemplo

O exemplo a seguir mostra a estrutura básica de como trabalhar com o join classe. Este exemplo usa a Concurrency::make_join função para criar um join objeto que recebe de três single_assignment objetos. Este exemplo calcula vários números de Fibonacci, que armazena cada resultado em um local diferente single_assignment objeto e, em seguida, imprime no console resulta que o join suspensões do objeto. Este exemplo é semelhante ao exemplo para o choice de classe, exceto que o join classe aguarda a todos os blocos de mensagem de origem receber uma mensagem.

// join-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
   if (n < 2)
      return n;
   return fibonacci(n-1) + fibonacci(n-2);
}

int wmain()
{
   // Holds the 35th Fibonacci number.
   single_assignment<int> fib35;
   // Holds the 37th Fibonacci number.
   single_assignment<int> fib37;
   // Holds half of the 42nd Fibonacci number.
   single_assignment<double> half_of_fib42;   

   // Create a join object that selects the values from each of the
   // single_assignment objects.
   auto join_all = make_join(&fib35, &fib37, &half_of_fib42);

   // Execute a few lengthy operations in parallel. Each operation sends its 
   // result to one of the single_assignment objects.
   parallel_invoke(
      [&fib35] { send(fib35, fibonacci(35)); },
      [&fib37] { send(fib37, fibonacci(37)); },
      [&half_of_fib42] { send(half_of_fib42, fibonacci(42) * 0.5); }
   );

   auto result = receive(join_all);
   wcout << L"fib35 = " << get<0>(result) << endl;
   wcout << L"fib37 = " << get<1>(result) << endl;
   wcout << L"half_of_fib42 = " << get<2>(result) << endl;
}

Esse exemplo produz a seguinte saída.

fib35 = 9227465
fib37 = 24157817
half_of_fib42 = 1.33957e+008

Este exemplo usa a Concurrency::parallel_invoke o algoritmo para calcular os números de Fibonacci em paralelo. Para obter mais informações sobre o parallel_invoke, consulte Algoritmos paralelos.

Para concluir exemplos que mostram como usar o join da classe, consulte Como: Selecione entre as tarefas concluídas e Demonstra Passo a passo: Usando a associação para evitar o Deadlock.

go to top

Timer de classe

O Concurrency::timer classe age como uma fonte de mensagem. A timer objeto envia uma mensagem para um destino após um período de tempo especificado tiver decorrido. O timer classe é útil quando você deve atrasar a enviar uma mensagem ou você deseja enviar uma mensagem em um intervalo regular.

O timer classe envia sua mensagem para apenas um destino. Se você definir a _PTarget parâmetro no construtor para NULL, mais tarde você pode especificar o destino chamando o Concurrency::ISource::link_target método.

A timer objeto pode ser repetidos ou não-repetição. Para criar um timer de repetição, passe true para o _Repeating parâmetro ao chamar o construtor. Caso contrário, passe false para o _Repeating parâmetro para criar um temporizador não repetição. Se o timer é repetição, ele envia a mesma mensagem para seu destino depois de cada intervalo.

A biblioteca de agentes cria timer objetos do estado não é iniciado. Para iniciar um objeto timer, chamar o Concurrency::timer::start método. Para interromper um timer de objeto, destruir o objeto ou a chamada a Concurrency::timer::stop método. Para pausar um timer de repetição, chame o Concurrency::timer::pause método.

Exemplo

O exemplo a seguir mostra a estrutura básica de como trabalhar com o timer classe. O exemplo usa timer e call objetos para relatar o andamento de uma operação demorada.

// timer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
   if (n < 2)
      return n;
   return fibonacci(n-1) + fibonacci(n-2);
}

int wmain()
{
   // Create a call object that prints characters that it receives 
   // to the console.
   call<wchar_t> print_character([](wchar_t c) {
      wcout << c;
   });

   // Create a timer object that sends the period (.) character to 
   // the call object every 100 milliseconds.
   timer<wchar_t> progress_timer(100u, L'.', &print_character, true);

   // Start the timer.
   wcout << L"Computing fib(42)";
   progress_timer.start();

   // Compute the 42nd Fibonacci number.
   int fib42 = fibonacci(42);

   // Stop the timer and print the result.
   progress_timer.stop();
   wcout << endl << L"result is " << fib42 << endl;
}

Este exemplo produz a saída de exemplo a seguir:

Computing fib(42)..................................................
result is 267914296

Para obter um exemplo completo que mostra como usar o timer da classe, consulte Como: Enviar uma mensagem em intervalos regulares.

go to top

Filtragem de mensagens

Quando você cria um objeto de bloco de mensagem, você pode fornecer um a função de filtro que determina se o bloco de mensagens aceita ou rejeita uma mensagem. Uma função de filtro é uma maneira útil para garantir que um bloco de mensagens recebe apenas certos valores.

O exemplo a seguir mostra como criar um unbounded_buffer objeto que usa uma função de filtro para aceitar somente números pares. O unbounded_buffer objeto rejeita números ímpares e, portanto, não se propaga a números ímpares para blocos de destino.

// filter-function.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an unbounded_buffer object that uses a filter
   // function to accept only even numbers.
   unbounded_buffer<int> accept_evens(
      [](int n) {
         return (n%2) == 0;
      });

   // Send a few values to the unbounded_buffer object.
   unsigned int accept_count = 0;
   for (int i = 0; i < 10; ++i)
   {
      // The asend function returns true only if the target
      // accepts the message. This enables us to determine
      // how many elements are stored in the unbounded_buffer
      // object.
      if (asend(accept_evens, i))
      {
         ++accept_count;
      }
   }

   // Print to the console each value that is stored in the 
   // unbounded_buffer object. The unbounded_buffer object should
   // contain only even numbers.
   while (accept_count > 0)
   {
      wcout << receive(accept_evens) << L' ';
      --accept_count;
   }
}

Esse exemplo produz a seguinte saída.

0 2 4 6 8

Uma função de filtro pode ser uma função lambda, um ponteiro de função ou um objeto de função. Cada função de filtro leva a uma das formas a seguir.

bool (_Type)
bool (_Type const &)

Para eliminar a cópia desnecessária de dados, use o segundo formulário quando você tem um tipo de agregação é propagado por valor.

Suporta de filtragem de mensagens de fluxo de dados modelo de programação, na qual os componentes executam computações quando recebem dados. Para obter exemplos que usam funções de filtro para controlar o fluxo de dados em uma rede de transmissão de mensagens, consulte Como: Use um filtro de bloco de mensagem, Demonstra Passo a passo: Criando um agente de fluxo de dados, e Demonstra Passo a passo: A criação de uma rede de processamento de imagens.

go to top

Reserva de mensagem

Mensagem de reserva permite que um bloco de mensagens reservar uma mensagem para uso posterior. Normalmente, a reserva de mensagem não é usada diretamente. No entanto, a mensagem de compreensão reserva pode ajudá compreender o comportamento de alguns dos tipos de bloco de mensagens predefinidas.

Considere a possibilidade de associações não greedy e greedy. Ambas usam reserva de mensagem para reservar mensagens para uso posterior. Um descrito anteriormente, uma associação não greedy recebe mensagens em duas fases. Durante a primeira fase, um não greedy join objeto espera por cada uma das suas fontes de receber uma mensagem. Uma associação não greedy tenta reservar cada uma dessas mensagens. Se ele pode reservar a cada mensagem, ele consome todas as mensagens e propaga a seu destino. Caso contrário, ele libera, ou cancela, as reservas de mensagem e aguarda novamente a cada fonte receber uma mensagem.

Uma associação greedy, que também lê as mensagens de entrada de um número de fontes, usa a reserva de mensagem para ler mensagens adicionais, enquanto espera para receber uma mensagem de cada fonte. Por exemplo, considere uma associação greedy que recebe mensagens em blocos de mensagem A e B. Se a associação greedy recebe duas mensagens de B, mas ainda não recebeu uma mensagem de A, a associação greedy salvará o identificador de mensagem exclusiva para a segunda mensagem de B. Depois que a associação greedy recebe uma mensagem de A e propaga essas mensagens, ele usa o identificador da mensagem salva para ver se a segunda mensagem de B ainda está disponível.

Ao implementar seus próprios tipos de bloco de mensagem personalizada, você pode usar a reserva de mensagem. Para obter um exemplo sobre como criar um tipo de bloco de mensagem personalizada, consulte Demonstra Passo a passo: Criar um bloco de mensagem personalizada.

go to top

Consulte também

Conceitos

Biblioteca de agentes assíncronos

Histórico de alterações

Date

History

Motivo

Agosto de 2010

Exemplos de básicos adicionados.

Aprimoramento de informações.