Blocos de mensagens assíncronos
A biblioteca dos agentes fornece vários tipos retornadas bloco o que permite propagar mensagens entre componentes de aplicativo em um modo seguro para threads. Esses tipos retornadas bloco são usados com mais frequência as rotinas mensagem- passando, como concurrency::send, concurrency::asend, concurrency::receive, e concurrency::try_receive. Para obter mais informações sobre a mensagem que passa as rotinas definidas pela biblioteca de agentes, consulte Funções de transmissão de mensagem.
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 da Mensagem
classe unbounded_buffer
Classe overwrite_buffer
Classe single_assignment
Classe da chamada
Classe transformer
Classe choice
Classes join e multitype_join
Classe timer
Filtragem de Mensagem
Reserva de Mensagem
Origens e Destinos
As origens e os destinos são dois participantes importantes de passagem da mensagem. Uma origem refere-se a um ponto de extremidade de comunicação que envia mensagens. Um destino se refere a um ponto de extremidade de comunicação que recebe mensagens. Você pode pensar em uma origem como um ponto de extremidade que você ler e um destino como um ponto de extremidade para que você grave. Os aplicativos se conectam as origens e os destinos em conjunto para formar redes da mensagem.
A biblioteca dos agentes usa duas classes abstratas para representar as origens e os destinos: concurrency::ISource e concurrency::ITarget. Os tipos de bloco de mensagem que atuam como fontes derivam de ISource; os tipos de bloco de mensagem que atuam como destinos derivam de ITarget. Os tipos do bloco de mensagem que atuam como origens e destinos derivam-se de ISource e de ITarget.
[Superior]
Propagação de Mensagem
A propagação de mensagem é o ato de enviar uma mensagem de um componente para outro. Quando um bloco da mensagem é oferecido uma mensagem, pode aceitar, diminua, ou adiar essa mensagem. As lojas em blocos de cada mensagem e transmitir mensagens de diferentes maneiras. Por exemplo, a classe de unbounded_buffer armazena um número ilimitado de mensagens, a classe de overwrite_buffer armazena uma única mensagem por vez, e a classe de transformador armazena uma versão modificada de cada mensagem. Esses tipos do bloco do sistema de mensagens são descritos mais detalhadamente mais adiante neste documento.
Quando um bloco de mensagem aceita uma mensagem, opcionalmente poderá executar o trabalho e, se o bloco de mensagem é uma origem, envia a mensagem resultante para outro membro da rede. Um bloco de mensagem pode usar uma função de filtro para diminuir as mensagens que não deseja receber. Os filtros são descritos com mais detalhes posteriormente neste tópico, na seção Filtragem de mensagem. Um bloco de mensagem que adiar uma mensagem pode reservar essa mensagem e consumir-la posterior. A reserva da mensagem é descrita com mais detalhes posteriormente neste tópico, na seção Reserva de mensagem.
A biblioteca dos agentes permite bloqueios da mensagem transmite o de forma assíncrona ou síncrona mensagens. Quando você envia uma mensagem para um bloco da mensagem de forma síncrona, por exemplo, usando a função de send , o tempo de execução bloqueia o contexto atual até que o bloco de destino aceita ou descartar a mensagem. Quando você envia uma mensagem para um bloco da mensagem de forma assíncrona, por exemplo, usando a função de asend , o tempo de execução oferece a mensagem ao destino, e se o destino aceita a mensagem, o tempo de execução agenda uma tarefa assíncrono que propague a mensagem ao destinatário. O tempo de execução usa tarefas de peso leve propagar mensagens de um modo cooperativa. Para obter mais informações sobre as tarefas de peso leve, consulte Agendador de tarefas (Tempo de Execução de Simultaneidade).
Os aplicativos se conectam as origens e os destinos em conjunto para formar redes da mensagem. Normalmente, você vinculará a rede e a chamada send ou asend para transmitir dados na rede. Para conectar um bloco da mensagem de origem para um destino, chame o método de concurrency::ISource::link_target . Para desativar um bloco de origem de um destino, chame o método de concurrency::ISource::unlink_target . Para desativar um bloco de origem de todos os destinos, chame o método de concurrency::ISource::unlink_targets . Quando um bloco de predefinido de mensagem nas folhas ou define o escopo, ele será destruído automaticamente desliga-se de todos os blocos de destino. Alguns tipos do bloco de mensagem restringem o número máximo de destinos a que podem gravar. A seção a seguir descreve as restrições que se aplicam a tipos predefinidos do bloco da mensagem.
[Superior]
Visão Geral dos Tipos de Bloco da Mensagem
A tabela a seguir descreve brevemente a função dos tipos importantes retornadas bloco.
unbounded_buffer
Armazena uma fila de mensagens.overwrite_buffer
Armazena uma mensagem à qual pode ser escrita e à leitura de várias vezes.single_assignment
Armazena uma mensagem que pode ser gravada em uma vez e leitura de várias vezes.chame
Executa o trabalho quando o recebe uma mensagem.transformador
Executa o trabalho quando o recebe dados e envia o resultado desse trabalho em outro bloco de destino. A classe de transformer pode atuar em diferentes tipos de entrada e saída.choice
Selecione a primeira mensagem disponível de um conjunto de fontes.junção e junção do múltiplo
Aguarde todas as mensagens a serem recebidas de um conjunto de fontes e combine as mensagens em uma mensagem para outro bloco da mensagem.timer
Envia uma mensagem para um bloco de destino em um intervalo normal.
Esses tipos retornadas bloco têm características diferentes que a tornam úteis em 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 destinatário de dados, ou ambos.
A ordem das mensagens: Se o bloco de mensagem mantém a ordem original na qual as mensagens são enviadas ou recebidas. Cada mensagem predefinida em blocos mantém a ordem original na qual enviar ou receber mensagens.
Contagem de origem: O número máximo de fontes do qual o bloco de mensagem pode ler.
Contagem de destino: O número máximo de destinos a que o bloco de mensagem pode gravar.
A tabela a seguir mostra como essas características se relacionam com o mais mensagem- bloco tipo.
Mensagem em blocos |
Tipo de propagação (origem, destino, ou ambos) |
Mensagem de solicitação (ordenado ou não ordenada) |
Contagem de origem |
Contagem de destino |
---|---|---|---|---|
unbounded_buffer |
Ambos |
Ordenado |
Ilimitado |
Ilimitado |
overwrite_buffer |
Ambos |
Ordenado |
Ilimitado |
Ilimitado |
single_assignment |
Ambos |
Ordenado |
Ilimitado |
Ilimitado |
call |
Destino |
Ordenado |
Ilimitado |
Não aplicável |
transformer |
Ambos |
Ordenado |
Ilimitado |
1 |
choice |
Ambos |
Ordenado |
10 |
1 |
join |
Ambos |
Ordenado |
Ilimitado |
1 |
multitype_join |
Ambos |
Ordenado |
10 |
1 |
timer |
Origem |
Não aplicável |
Não aplicável |
1 |
As seções a seguir descrevem os tipos retornadas bloco com mais detalhes.
[Superior]
classe unbounded_buffer
A classe de concurrency::unbounded_buffer representa uma estrutura assíncrona de uso geral da mensagem. Essa classe armazena primeiro no, primeiro para fora da fila (FIFO) de mensagens que podem ser escritas pela várias fontes ou à leitura por vários destinos. Quando um destino recebe uma mensagem de um objeto de unbounded_buffer , essa mensagem será removida da fila de mensagens. Em virtude disso, embora um objeto de unbounded_buffer pode ter vários destinos, somente um destino receberá cada mensagem. A classe de unbounded_buffer é ú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 a classe de unbounded_buffer . Esse exemplo envia três valores a um objeto de unbounded_buffer e depois ler a backups dos 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;
}
Este exemplo gera a seguinte saída:
Para obter um exemplo completo que mostra como usar a classe de unbounded_buffer , considere Como implementar vários padrões de produtor-consumidor.
[Superior]
Classe overwrite_buffer
A classe de concurrency::overwrite_buffer é semelhante à classe de unbounded_buffer , exceto que a repositórios de um objeto de overwrite_buffer apenas uma mensagem. Além disso, quando um destino recebe uma mensagem de um objeto de overwrite_buffer , essa mensagem não será removida do buffer. Em virtude disso, vários destinos recebem uma cópia da mensagem.
A classe de overwrite_buffer é várias mensagens para outro componente, mas esse componente precisa úteis quando você quiser passar apenas o valor mais recente. Essa classe também é útil quando você desejar transmitir uma mensagem aos vários componentes.
Exemplo
O exemplo a seguir mostra a estrutura básica de como trabalhar com a classe de overwrite_buffer . Esse exemplo envia três valores a um objeto de overwrite _buffer e depois ler ao valor atual do mesmo objeto três vezes. Este exemplo é semelhante ao exemplo da classe de unbounded_buffer . No entanto, a classe de overwrite_buffer armazena apenas uma mensagem. Além disso, o tempo de execução não remove a mensagem de um objeto de overwrite_buffer depois lido.
// 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;
}
Este exemplo gera a seguinte saída:
Para obter um exemplo completo que mostra como usar a classe de overwrite_buffer , considere Como implementar vários padrões de produtor-consumidor.
[Superior]
Classe single_assignment
A classe de concurrency::single_assignment é semelhante à classe de overwrite_buffer , exceto que um objeto de single_assignment pode ser gravado em apenas uma vez. Como a classe de overwrite_buffer , quando um destino recebe uma mensagem de um objeto de single_assignment , que a mensagem não será removida do objeto. Em virtude disso, vários destinos recebem uma cópia da mensagem. A classe de single_assignment é útil quando você desejar transmitir uma mensagem aos vários componentes.
Exemplo
O exemplo a seguir mostra a estrutura básica de como trabalhar com a classe de single_assignment . Esse exemplo envia três valores a um objeto de single_assignment e depois ler ao valor atual do mesmo objeto três vezes. Este exemplo é semelhante ao exemplo da classe de overwrite_buffer . Embora as classes de overwrite_buffer e de single_assignment armazenam uma única mensagem, a classe de single_assignment pode ser gravada 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;
}
Este exemplo gera a seguinte saída:
Para obter um exemplo completo que mostra como usar a classe de single_assignment , considere Instruções passo a passo: implementando futuros.
[Superior]
Classe da chamada
A classe de concurrency::call atua como um destinatário da mensagem que executa uma função de trabalho quando o recebe dados. Essa função de trabalho pode ser uma expressão de lambda, um objeto de função, ou um ponteiro de função. Um objeto de call se comporta diferentemente de uma chamada de função comum porque atua em paralelo com outros componentes que lhe enviam mensagens. Se um objeto de call está executando o trabalho quando o recebe uma mensagem, adiciona essa mensagem para uma fila. Cada objeto de call processa mensagens enfileiradas na ordem em que são recebidas.
Exemplo
O exemplo a seguir mostra a estrutura básica de como trabalhar com a classe de call . Este exemplo cria um objeto de call que imprime cada valor que recebe ao console. O exemplo envia três valores ao objeto de call . Como o objeto de call processa as mensagens em um thread separado, este exemplo também usa uma variável e vez de um objeto de evento para garantir que os processos de objeto de call todas as mensagens antes da função de wmain retornam.
// 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();
}
Este exemplo gera a seguinte saída:
Para obter um exemplo completo que mostra como usar a classe de call , considere Como fornecer funções de trabalho para as classes call e transformer.
[Superior]
Classe transformer
A classe de concurrency::transformer atua como um destinatário da mensagem e como um remetente da mensagem. A classe de transformer é semelhante à classe de call como executar uma função de trabalho definida pelo usuário quando o recebe dados. No entanto, a classe de transformer também envia o resultado da função de trabalho para objetos do destinatário. Como um objeto de call , um objeto de transformer atua em paralelo com outros componentes que lhe enviam mensagens. Se um objeto de transformer está executando o trabalho quando o recebe uma mensagem, adiciona essa mensagem para uma fila. Cada objeto de transformer processa as mensagens enfileiradas na ordem em que são recebidas.
A classe de transformer envia a mensagem a um destino. Se você definir o parâmetro no construtor de _PTarget a NULL, depois poderá especificar o destino chamando o método de concurrency::link_target .
Ao contrário de todos os outros assíncronas do bloco de mensagem que são fornecidos pela biblioteca de agentes, a classe de transformer pode atuar em diferentes tipos de entrada e saída. Essa capacidade de transformar dados de um tipo para outro torna a classe de transformer um componente fundamental para várias redes simultâneas. Além disso, você pode adicionar uma funcionalidade paralela mais refinado na função de trabalho de um objeto de transformer .
Exemplo
O exemplo a seguir mostra a estrutura básica de como trabalhar com a classe de transformer . Este exemplo cria um objeto de transformer que multiplica cada valor de int de entrada por 0,33 para gerar um valor de double como saída. O exemplo pega os valores transformados do mesmo objeto de transformer e imprime-os ao 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;
}
Este exemplo gera a seguinte saída:
Para obter um exemplo completo que mostra como usar a classe de transformer , considere Como usar transformador em um pipeline de dados.
[Superior]
Classe choice
A classe de concurrency::choice selecione a primeira mensagem disponível de um conjunto de fontes. A classe de choice 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 de um objeto bem escolhido é semelhante a chamar a função de API WaitForMultipleObjects do windows quando tem o parâmetro de bWaitAll definido como FALSE. No entanto, os próprios dados ao evento do padrão da classe de choice em vez de um objeto externo de sincronização.
Normalmente, você usa a classe de choice junto com a função de concurrency::receive para direcionar o fluxo de controle em seu aplicativo. Use a classe de choice quando você precisa selecionar entre os buffers de mensagem que têm tipos diferentes. Use a classe de single_assignment quando você precisa selecionar entre os buffers de mensagem que têm o mesmo tipo.
A ordem na qual você vincular origens a um objeto de choice é importante porque pode determinar qual mensagem é selecionada. Por exemplo, suponha que você vincular os buffers de várias mensagens que já contém uma mensagem para um objeto de choice . O objeto de choice seleciona a mensagem da primeira origem que está vinculado. Depois que você vincular todas as origens, o objeto de choice preserva a ordem na qual cada origem recebe uma mensagem.
Exemplo
O exemplo a seguir mostra a estrutura básica de como trabalhar com a classe de choice . Este exemplo usa a função de concurrency::make_choice para criar um objeto de choice que seleciona entre três blocos da mensagem. No exemplo computa vários números de Fibonacci e armazena cada resultado em um bloco diferente da mensagem. O exemplo a seguir imprime no console uma mensagem com base na operação que foi concluída primeiro.
// 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 gerencia a seguinte saída de exemplo:
Como a tarefa que calcula o númeroº de 35 Fibonacci não é garantida para concluir primeiro, a saída deste exemplo podem variar.
Este exemplo usa o algoritmo de concurrency::parallel_invoke para computar em paralelo os números de Fibonacci. Para obter mais informações sobre parallel_invoke, consulte Algoritmos paralelos.
Para obter um exemplo completo que mostra como usar a classe de choice , considere Como selecionar tarefas concluídas.
[Superior]
Classes join e multitype_join
As classes de concurrency::join e de concurrency::multitype_join permitem esperar cada membro de um conjunto de fontes para receber uma mensagem. A classe de join atua nos objetos de origem que têm um tipo de mensagem comuns. A classe de multitype_join atua nos objetos de origem que podem ter tipos diferentes de mensagem.
Ler de um objeto de join ou de multitype_join é semelhante a chamar a função de API WaitForMultipleObjects do windows quando tem o parâmetro de bWaitAll definido como TRUE. No entanto, exatamente como um objeto de choice , join e os objetos de multitype_join usam um mecanismo de evento que associa os dados para o evento próprios em vez de um objeto externo de sincronização.
Ler de um objeto de join gerencie um objeto de std::vector . Ler de um objeto de multitype_join gerencie um objeto de std::tuple . Os elementos aparecem nesses objetos na mesma ordem que os buffers correspondentes de origem são vinculados ao objeto de join ou de multitype_join . Como a ordem na qual você vincular buffers de origem para um objeto de join ou de multitype_join é associado à ordem dos elementos em vector ou no objeto resultante de tuple , recomendamos que você não poderá desvincular um buffer existente de origem de uma junção. Isso pode resultar em comportamento não especificado.
Junções Greedy Contra Não Greedy
As classes de join e de multitype_join dão suporte ao conceito de junções ávidos e não ávidos. Uma junção ávido aceita uma mensagem de cada uma de suas fontes como as mensagens se tornam disponíveis até que toda a mensagem está disponível. Uma junção não ávido recebe mensagens em duas fases. Primeiro, uma junção não ávido espera até que seja fornecido uma mensagem de cada uma de suas fontes. Segundo, todas as mensagens de origem estiverem disponíveis, uma junção não ávido tentam permitir cada uma dessas mensagens. Se pode reservar cada mensagem, consome todas as mensagens e propagar-las em seu destino. Se não, sem, ou cancelamentos, as reservas da mensagem e espera-se novamente cada origem para receber uma mensagem.
Junções ávidos executam junções melhor do que não ávidos como aceitem mensagens imediatamente. No entanto, em casos raros, as junções ávidos podem resultar em deadlock. Use uma junção não ávido quando você tiver várias junções que contêm um ou mais objetos compartilhados de origem.
Exemplo
O exemplo a seguir mostra a estrutura básica de como trabalhar com a classe de join . Este exemplo usa a função de concurrency::make_join para criar um objeto de join que recebe de três objetos de single_assignment . Esse exemplo calcula vários números de Fibonacci, armazena cada resultado em um objeto diferente de single_assignment , e imprime ao console cada resultado que o objeto de join mantém. Este exemplo é semelhante ao exemplo da classe de choice , exceto que as esperas da classe de join para o qual todos os blocos da 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;
}
Este exemplo gera a seguinte saída:
Este exemplo usa o algoritmo de concurrency::parallel_invoke para computar em paralelo os números de Fibonacci. Para obter mais informações sobre parallel_invoke, consulte Algoritmos paralelos.
Para os exemplos completos que mostram como usar a classe de join , consulte Como selecionar tarefas concluídas e Instruções passo a passo: usando join para Evitar Deadlock.
[Superior]
Classe timer
A classe de concurrency::timer atua como uma origem da mensagem. Um objeto de timer envia uma mensagem para um destino após um período de tempo decorrido especificado. A classe de timer é útil quando você atrasar deve enviar uma mensagem ou você deseja enviar uma mensagem em um intervalo normal.
A classe de timer envia a mensagem a apenas um destino. Se você definir o parâmetro no construtor de _PTarget a NULL, depois poderá especificar o destino chamando o método de concurrency::ISource::link_target .
Um objeto de timer pode repetir ou não ser repetido. Para criar um timer de repetição, true passar para o parâmetro de _Repeating quando você chama o construtor. Caso contrário, false passar para o parâmetro de _Repeating crie um timer não repetente. Se o timer está repetindo, envia a mesma mensagem ao destino depois de cada intervalo.
A biblioteca dos agentes cria objetos de timer em estado não iniciado. Para iniciar um objeto de timer, chame o método de concurrency::timer::start . Para interromper um objeto de timer , destruir o objeto ou chame o método de concurrency::timer::stop . Para pausar um timer de repetição, chame o método de concurrency::timer::pause .
Exemplo
O exemplo a seguir mostra a estrutura básica de como trabalhar com a classe de timer . O exemplo usa timer e objetos de call para informar o progresso 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 gerencia a seguinte saída de exemplo:
Para obter um exemplo completo que mostra como usar a classe de timer , considere Como enviar uma mensagem em um intervalo regular.
[Superior]
Filtragem de Mensagem
Quando você cria um objeto do bloco da mensagem, você pode fornecer uma função de filtro que determina se o pacote de mensagem aceita ou descarta uma mensagem. Uma função de filtro é uma maneira útil para garantir que um bloco de mensagem recebe apenas determinados valores.
O exemplo a seguir mostra como criar um objeto de unbounded_buffer que usa uma função de filtro para aceitar apenas mesmo números. O objeto de unbounded_buffer descarta números não correspondentes e, consequentemente não propaga números não correspondentes aos 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;
}
}
Este exemplo gera a seguinte saída:
Uma função de filtro pode ser uma função de lambda, um ponteiro de função, ou um objeto da função. Cada função de filtro usa um dos seguintes formulários.
Para eliminar a cópia desnecessária de dados, use o segundo formulário quando você tem um tipo de agregação que seja propagado pelo valor.
A filtragem de mensagem oferece suporte ao modelo de programação fluxo de dados, em que os componentes executam cálculos quando recebem dados. Para obter exemplos que usam funções de filtro para controlar o fluxo de dados em uma mensagem que passe na rede, consulte Como usar um filtro de bloco de mensagens, Instruções passo a passo: criando um agente de fluxo de dados, e Instruções passo a passo: criando uma rede de processamento de imagem.
[Superior]
Reserva de Mensagem
A reserva de mensagem permite que um bloco de mensagem a ser reservada para uso posterior uma mensagem. Normalmente, a reserva da mensagem não é usada diretamente. No entanto, a reserva compreensão da mensagem pode ajudá-lo a entender melhor o comportamento de alguns dos tipos predefinidos do bloco da mensagem.
Considere as junções não ávidos e ávidos. Ambos usam a reserva da mensagem a ser reservada para uso posterior mensagens. Um descrito anteriormente, uma junção não ávido recebe mensagens em duas fases. Durante a primeira fase, um objeto não ávido de join espera cada uma de suas fontes para receber uma mensagem. Uma junção não ávido tente permitir cada uma dessas mensagens. Se pode reservar cada mensagem, consome todas as mensagens e propagar-las em seu destino. Se não, sem, ou cancelamentos, as reservas da mensagem e espera-se novamente cada origem para receber uma mensagem.
Uma junção ávido, que também lê mensagens de entrada de diversas fontes, usa a reserva de mensagem para ler mensagens adicionais quando esperar para receber uma mensagem de cada origem. Por exemplo, considere uma junção ávido que receba mensagens dos blocos A e Bda mensagem. Se a junção ávido recebe duas mensagens de B mas o ainda não recebeu uma mensagem de A, a junção ávido salva o identificador de mensagem exclusivo para a segunda mensagem de B. Depois que a junção ávido recebe uma mensagem de A e propaga out essas mensagens, usa o identificador da mensagem para verificar se a segunda mensagem de B ainda está disponível.
Você pode usar a reserva de mensagem quando você implementa seus próprios tipos personalizadas do bloco da mensagem. Para obter um exemplo de como criar uma mensagem personalizada em blocos, consulte Instruções passo a passo: criando um bloco de mensagens personalizado.
[Superior]