Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
A Biblioteca de Agentes fornece vários tipos de bloco de mensagens que permitem propagar mensagens entre os componentes do aplicativo de maneira segura para threads. Esses tipos de bloco de mensagens são frequentemente usados com as várias rotinas de passagem de mensagens, como concurrency::send, concurrency::asend, concurrency::receive e concurrency::try_receive. Para obter mais informações sobre as rotinas de passagem de mensagens definidas pela Biblioteca de agentes, consulte Funções de passagem de mensagens.
Secções
Este tópico contém as seguintes seções:
Fontes e Alvos
As fontes e os alvos são dois participantes importantes na transmissão de mensagens. Uma fonte refere-se a um ponto de extremidade de comunicação que envia mensagens. Um destino refere-se a uma extremidade de comunicação que recebe mensagens. Pode-se pensar numa fonte como um ponto de extremidade de onde se lê e num destino como um ponto de extremidade para o qual se escreve. Os aplicativos conectam fontes e destinos juntos para formar redes de mensagens.
A Biblioteca de Agentes usa duas classes abstratas para representar fontes e destinos: simultaneidade::ISource e simultaneidade::ITarget. Tipos de bloco de mensagem que atuam como fontes derivam de ISource; tipos de bloco de mensagem que atuam como destinos derivam de ITarget. Os tipos de blocos de mensagens que atuam como fontes e destinos derivam tanto de ISource quanto de ITarget.
[Topo]
Propagação de mensagens
A propagação de mensagens é o ato de enviar uma mensagem de um componente para outro. Quando uma mensagem é oferecida a um bloco de mensagens, ele pode aceitar, recusar ou adiar essa mensagem. Cada tipo de bloco de mensagens armazena e transmite mensagens de maneiras diferentes. Por exemplo, a classe armazena unbounded_buffer um número ilimitado de mensagens, a overwrite_buffer classe armazena uma única mensagem de cada vez e a classe transformer armazena uma versão alterada de cada mensagem. Esses tipos de bloco de mensagem são descritos com mais detalhes mais adiante neste documento.
Quando um bloco de mensagens aceita uma mensagem, ele pode, opcionalmente, executar trabalho e, se o bloco de mensagens for uma fonte, passar a mensagem resultante para outro membro da rede. Um bloco de mensagens pode usar uma função de filtro para recusar mensagens que não deseja receber. Os filtros são descritos com mais detalhes mais adiante neste tópico, na seção Filtragem de mensagens. Um bloco de mensagem que adia uma mensagem pode reservar essa mensagem e consumi-la posteriormente. A reserva de mensagens é descrita com mais detalhes mais adiante neste tópico, na seção Reserva de mensagens.
A Biblioteca de Agentes permite que os blocos de mensagens passem mensagens de forma assíncrona ou síncrona. Quando você passa uma mensagem para um bloco de mensagens de forma síncrona, por exemplo, usando a send função, o tempo de execução bloqueia o contexto atual até que o bloco de destino aceite ou rejeite a mensagem. Quando você passa uma mensagem para um bloco de mensagens de forma assíncrona, por exemplo, usando a asend função, o tempo de execução oferece a mensagem para o destino e, se o destino aceitar a mensagem, o tempo de execução agenda uma tarefa assíncrona que propaga a mensagem para o recetor. O tempo de execução do sistema usa tarefas leves para propagar mensagens de forma cooperativa. Para obter mais informações sobre tarefas leves, consulte Agendador de tarefas.
Os aplicativos conectam fontes e destinos juntos para formar redes de mensagens. Normalmente, vincula a rede e chama send ou asend para passar dados para a rede. Para conectar um bloco de mensagem de origem a um destino, chame o método concurrency::ISource::link_target . Para desconectar um bloco de origem de um destino, chame o método concurrency::ISource::unlink_target . Para desconectar um bloco de origem de todos os seus destinos, chame o método concurrency::ISource::unlink_targets . Quando um dos tipos de bloco de mensagem predefinidos sai do escopo ou é destruído, ele se desconecta automaticamente de qualquer bloco de destino. Alguns tipos de bloco de mensagens restringem o número máximo de destinos nos quais podem gravar. A seção a seguir descreve as restrições que se aplicam aos tipos de bloco de mensagem predefinidos.
[Topo]
Visão geral dos tipos de bloco de mensagens
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 lida várias vezes.
single_assignment
Armazena uma mensagem que pode ser gravada uma vez e lida várias vezes.
ligar para
Executa o trabalho quando recebe uma mensagem.
transformador
Executa o trabalho quando recebe dados e envia o resultado desse trabalho para outro bloco de destino. A transformer classe pode atuar em diferentes tipos de entrada e saída.
escolha
Seleciona a primeira mensagem disponível de um conjunto de fontes.
junção e junção multitipo
Aguarde até que todas as mensagens sejam recebidas de um conjunto de fontes e, em seguida, combine as mensagens em uma mensagem para outro bloco de mensagens.
temporizador
Envia uma mensagem para um bloco de destino em um intervalo regular.
Esses tipos de bloco de mensagem têm características diferentes que os tornam úteis para diferentes situações. Estas são algumas das características:
Tipo de propagação: Se o bloco de mensagens atua como uma fonte de dados, um recetor de dados ou ambos.
Ordenação de mensagens: se o bloco de mensagens mantém a ordem original em que as mensagens são enviadas ou recebidas. Cada tipo de bloco de mensagens predefinido mantém a ordem original em que envia ou recebe mensagens.
Contagem de fontes: o número máximo de fontes das quais o bloco de mensagens pode ler.
Contagem de destinos: o número máximo de destinos nos quais o bloco de mensagens pode gravar.
A tabela a seguir mostra como essas características se relacionam com os vários tipos de bloco de mensagem.
| Tipo de bloco de mensagem | Tipo de propagação (origem, destino ou ambos) | Ordenação de mensagem (Ordenado ou Não Ordenado) | Contagem de fontes | Contagem de alvos |
|---|---|---|---|---|
unbounded_buffer |
Ambos | Encomendado | Sem limites | Sem limites |
overwrite_buffer |
Ambos | Encomendado | Sem limites | Sem limites |
single_assignment |
Ambos | Encomendado | Sem limites | Sem limites |
call |
Objetivo | Encomendado | Sem limites | Não Aplicável |
transformer |
Ambos | Encomendado | Sem limites | 1 |
choice |
Ambos | Encomendado | 10 | 1 |
join |
Ambos | Encomendado | Sem limites | 1 |
multitype_join |
Ambos | Encomendado | 10 | 1 |
timer |
Fonte | Não Aplicável | Não Aplicável | 1 |
As seções a seguir descrevem os tipos de bloco de mensagem com mais detalhes.
[Topo]
Classe unbounded_buffer
A classe concurrency::unbounded_buffer representa uma estrutura de mensagens assíncrona de uso geral. A classe armazena uma fila FIFO (first in, first out) de mensagens que podem ser escritas por múltiplas fontes ou lidas por múltiplos destinos. Quando um destino recebe uma mensagem de um unbounded_buffer objeto, essa mensagem é removida da fila de mensagens. Portanto, embora um unbounded_buffer objeto possa ter vários destinos, apenas um destino receberá cada mensagem. A classe 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 unbounded_buffer classe. Este exemplo envia três valores para um unbounded_buffer objeto e, em seguida, lê esses valores de volta 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 produz a seguinte saída:
334455
Para obter um exemplo completo que mostra como usar a unbounded_buffer classe, consulte Como implementar vários padrões Producer-Consumer.
[Topo]
overwrite_buffer Classe
A classe concurrency::overwrite_buffer é semelhante à classe unbounded_buffer, exceto que um objeto overwrite_buffer armazena apenas uma mensagem. Além disso, quando um destino recebe uma mensagem de um overwrite_buffer objeto, essa mensagem não é removida do buffer. Portanto, vários destinos recebem uma cópia da mensagem.
A overwrite_buffer classe é útil quando você deseja passar várias mensagens para outro componente, mas esse componente precisa apenas do valor mais recente. Essa classe também é ú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 a overwrite_buffer classe. Este exemplo envia três valores para um overwrite _buffer objeto e, em seguida, lê o valor atual do mesmo objeto três vezes. Este exemplo é semelhante ao exemplo da unbounded_buffer classe. No entanto, a classe armazena overwrite_buffer apenas uma mensagem. Além disso, o tempo de execução não remove a mensagem de um overwrite_buffer objeto depois que ele é 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 produz a seguinte saída:
555555
Para obter um exemplo completo que mostra como usar a overwrite_buffer classe, consulte Como implementar vários padrões Producer-Consumer.
[Topo]
classe de atribuição única
A classe concurrency::single_assignment é semelhante à overwrite_buffer classe, exceto que um single_assignment objeto pode ser gravado apenas uma vez. Como a classe overwrite_buffer, quando um destino recebe uma mensagem de um objeto single_assignment, essa mensagem não é removida desse objeto. Portanto, vários destinos recebem uma cópia da mensagem. A 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 a single_assignment classe. Este exemplo envia três valores para um single_assignment objeto e, em seguida, lê o valor atual do mesmo objeto três vezes. Este exemplo é semelhante ao exemplo da overwrite_buffer classe. Embora ambas as classes overwrite_buffer e single_assignment armazenem uma única mensagem, a classe single_assignment pode ser gravada 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 produz a seguinte saída:
333333
Para obter um exemplo completo que mostra como usar a single_assignment classe, consulte Passo a passo: Implementando futuros.
[Topo]
Chamar Classe
A classe concurrency::call atua como um recetor de mensagem que executa uma função de trabalho quando recebe dados. Essa função de trabalho pode ser uma expressão lambda, um objeto de função ou um ponteiro de função. Um call objeto se comporta de forma diferente de uma chamada de função comum porque age em paralelo com outros componentes que enviam mensagens para ele. Se um call objeto estiver executando trabalho quando receber uma mensagem, ele adicionará essa mensagem a uma fila. Cada call objeto 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 call classe. Este exemplo cria um call objeto que imprime cada valor que recebe no console. Em seguida, o exemplo envia três valores para o call objeto. Como o objeto processa call mensagens em um thread separado, este exemplo também usa uma variável de contador e um objeto de evento para garantir que o objeto processe call todas as mensagens antes que a wmain função retorne.
// 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 produz a seguinte saída:
334455
Para obter um exemplo completo que mostra como usar a call classe, consulte Como: Fornecer funções de trabalho para as classes de chamada e transformador.
[Topo]
transformador Classe
A classe concurrency::transformer atua como um recetor de mensagem e como um remetente de mensagem. A transformer classe é semelhante à call classe porque executa uma função de trabalho definida pelo usuário quando recebe dados. No entanto, a transformer classe também envia o resultado da função de trabalho para objetos recetores. Como um call objeto, um transformer objeto age em paralelo com outros componentes que enviam mensagens para ele. Se um transformer objeto estiver executando trabalho quando receber uma mensagem, ele adicionará essa mensagem a uma fila. Cada transformer objeto processa suas mensagens enfileiradas na ordem em que são recebidas.
A transformer classe envia sua mensagem para um destino. Se você definir o _PTarget parâmetro no construtor como NULL, poderá especificar posteriormente o destino chamando o método concurrency::link_target .
Ao contrário de todos os outros tipos de bloco de mensagens assíncronas fornecidos pela Biblioteca de agentes, a transformer classe pode atuar em diferentes tipos de entrada e saída. Essa capacidade de transformar dados de um tipo para outro torna a transformer classe um componente-chave em muitas redes simultâneas. Além disso, você pode adicionar mais funcionalidade 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 a transformer classe. Este exemplo cria um transformer objeto que multiplica cada valor de entrada int por 0,33 para produzir um double valor como saída. O exemplo recebe os valores transformados do mesmo transformer objeto e os 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;
}
Este exemplo produz a seguinte saída:
10.8914.5218.15
Para obter um exemplo completo que mostra como usar a classe, consulte transformer.
[Topo]
Escolha de classe
A classe concurrency::choice seleciona a primeira mensagem disponível de um conjunto de fontes. A 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 fluxo de dados e fluxo de controle).
A leitura de um objeto de escolha é semelhante a chamar a função WaitForMultipleObjects de API do Windows quando ela tem o bWaitAll parâmetro definido como FALSE. No entanto, a choice classe vincula dados ao próprio evento em vez de a um objeto de sincronização externo.
Normalmente, você usa a choice classe junto com a função concurrency::receive para direcionar o fluxo de controle em seu aplicativo. Use a choice classe quando tiver que selecionar entre buffers de mensagens que têm tipos diferentes. Use a single_assignment classe quando você tiver que selecionar entre buffers de mensagens que têm o mesmo tipo.
A ordem em que você vincula fontes a um choice objeto é importante porque pode determinar qual mensagem está selecionada. Por exemplo, considere o caso em que você vincula vários buffers de mensagens que já contêm uma mensagem a um choice objeto. O choice objeto seleciona a mensagem da primeira fonte à qual está vinculado. 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 a choice classe. Este exemplo usa a função concurrency::make_choice para criar um choice objeto que seleciona entre três blocos de mensagem. O exemplo então calcula vários números de Fibonacci e armazena cada resultado em um bloco de mensagem diferente. Em seguida, o exemplo imprime no console uma mensagem baseada na operação 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 produz a seguinte saída:
fib35 received its value first. Result = 9227465
Como não é garantido que a tarefa que calcula o 35ºnúmero de Fibonacci termine primeiro, a saída deste exemplo pode variar.
Este exemplo usa o algoritmo concurrency::parallel_invoke 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 a choice classe, consulte Como selecionar entre tarefas concluídas.
[Topo]
participar e multitype_join Classes
As classes concurrency::join e concurrency::multitype_join permitem que você aguarde que cada membro de um conjunto de fontes receba uma mensagem. A join classe atua em objetos de origem que têm um tipo de mensagem comum. A multitype_join classe atua em objetos de origem que podem ter diferentes tipos de mensagem.
A leitura de um join ou multitype_join objeto é semelhante a chamar a função WaitForMultipleObjects de API do Windows quando ela tem o bWaitAll parâmetro definido como TRUE. No entanto, assim como um choice objeto, join e multitype_join os objetos usam um mecanismo de evento que vincula dados ao próprio evento em vez de a um objeto de sincronização externo.
A leitura de um join objeto produz um objeto std::vector. A leitura de um multitype_join objeto produz um objeto std::tuple . Os elementos aparecem nesses objetos na mesma ordem em que seus buffers de origem correspondentes estão vinculados ao join objeto ou multitype_join . Como a ordem na qual se liga buffers de origem a um join ou multitype_join objeto está associada à ordem dos elementos no objeto vector ou tuple resultante, recomendamos que não se desvincule um buffer de origem existente de uma associação. Isso pode resultar em um comportamento não especificado.
Junções gananciosas versus não gananciosas
As join classes e multitype_join apoiam o conceito de junções gananciosas e não gananciosas. Uma junção gananciosa aceita uma mensagem de cada uma das suas fontes à medida que as mensagens se tornam disponíveis até que todas as mensagens fiquem disponíveis. Uma junção não gananciosa recebe mensagens em duas fases. Primeiro, uma adesão não gananciosa espera até que lhe seja oferecida uma mensagem de cada uma das suas fontes. Em segundo lugar, depois que todas as mensagens de origem estão disponíveis, uma junção não gananciosa tenta reservar cada uma dessas mensagens. Se puder reservar cada mensagem, consome todas as mensagens e propaga-as para o seu destino. Caso contrário, ele libera ou cancela as reservas de mensagens e novamente aguarda que cada fonte receba uma mensagem.
As junções gananciosas têm um desempenho melhor do que as junções não gananciosas porque aceitam mensagens imediatamente. No entanto, em casos raros, junções gananciosas podem levar a impasses. Use uma junção não gananciosa quando tiver várias junções que contenham um ou mais objetos de origem compartilhados.
Exemplo
O exemplo a seguir mostra a estrutura básica de como trabalhar com a join classe. Este exemplo usa a função concurrency::make_join para criar um join objeto que recebe de três single_assignment objetos. Este exemplo calcula vários números de Fibonacci, armazena cada resultado em um objeto diferente single_assignment e, em seguida, imprime no console cada resultado que o join objeto retém. Este exemplo é semelhante ao exemplo da choice classe, exceto que a join classe aguarda que todos os blocos de mensagem de origem recebam 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 produz a seguinte saída:
fib35 = 9227465fib37 = 24157817half_of_fib42 = 1.33957e+008
Este exemplo usa o algoritmo concurrency::parallel_invoke para calcular os números de Fibonacci em paralelo. Para obter mais informações sobre o parallel_invoke, consulte Algoritmos Paralelos.
Para obter exemplos completos que mostram como usar a join classe, consulte Como: Selecionar entre tarefas concluídas e Passo a passo: Usando a junção para evitar impasse.
[Topo]
Classe do temporizador
A classe concurrency::timer atua como uma fonte de mensagem. Um timer objeto envia uma mensagem para um destino após um período de tempo especificado. A timer classe é útil quando você deve atrasar o envio de uma mensagem ou deseja enviar uma mensagem em um intervalo regular.
A timer classe envia sua mensagem para apenas um destino. Se você definir o _PTarget parâmetro no construtor como NULL, poderá especificar posteriormente o destino chamando o método concurrency::ISource::link_target .
Um timer objeto pode ser repetitivo ou não repetitivo. Para criar um temporizador de repetição, passe true para o parâmetro _Repeating quando chamar o construtor. Caso contrário, passe false para o _Repeating parâmetro para criar um temporizador não repetitivo. Se o temporizador estiver a repetir, enviará a mesma mensagem para o seu destino após cada intervalo.
A Biblioteca de Agentes cria timer objetos no estado não iniciado. Para iniciar um objeto de temporizador, chame o método concurrency::timer::start . Para parar um timer objeto, destrua o objeto ou chame o método concurrency::timer::stop . Para pausar um temporizador recorrente, chame o método concurrency::timer::pause.
Exemplo
O exemplo a seguir mostra a estrutura básica de como trabalhar com a timer classe. O exemplo usa timer e call objetos para relatar o progresso de uma operação longa.
// 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 seguinte saída:
Computing fib(42)..................................................result is 267914296
Para obter um exemplo completo que mostra como usar a timer classe, consulte Como enviar uma mensagem em um intervalo regular.
[Topo]
Filtragem de mensagens
Ao criar um objeto de bloco de mensagem, você pode fornecer uma função de filtro que determina se o bloco de mensagem aceita ou rejeita uma mensagem. Uma função de filtro é uma maneira útil de garantir que um bloco de mensagens receba apenas determinados valores.
O exemplo a seguir mostra como criar um unbounded_buffer objeto que usa uma função de filtro para aceitar apenas números pares. O unbounded_buffer objeto rejeita números ímpares e, portanto, não propaga números ímpares para seus 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 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 assume uma das seguintes formas.
bool (T)
bool (T const &)
Para eliminar a cópia desnecessária de dados, use o segundo formulário quando tiver um tipo agregado propagado por valor.
A filtragem de mensagens suporta o modelo de programação de fluxo de dados , no qual 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 rede de passagem de mensagens, consulte Como usar um filtro de bloco de mensagens, Passo a passo: Criando um agente de fluxo de dados e Passo a passo: Criando uma rede Image-Processing.
[Topo]
Reserva de Mensagens
A reserva de mensagens permite que um bloco de mensagens reserve uma mensagem para uso posterior. Normalmente, a reserva de mensagens não é usada diretamente. No entanto, compreender a reserva de mensagens pode ajudá-lo a entender melhor o comportamento de alguns dos tipos de bloco de mensagens predefinidos.
Considere junções não gananciosas e gananciosas. Ambos usam reserva de mensagens para reservar mensagens para uso posterior. A descrito anteriormente, uma junção não gananciosa recebe mensagens em duas fases. Durante a primeira fase, um objeto não ganancioso join espera que cada uma de suas fontes receba uma mensagem. Uma união não gananciosa, então, tenta reservar cada uma dessas mensagens. Se puder reservar cada mensagem, consome todas as mensagens e propaga-as para o seu destino. Caso contrário, ele libera ou cancela as reservas de mensagens e novamente aguarda que cada fonte receba uma mensagem.
Uma junção gananciosa, que também lê mensagens de entrada provenientes de várias fontes, utiliza a reserva de mensagens para ler mensagens adicionais enquanto espera por uma mensagem de cada fonte. Por exemplo, considere uma junção voraz que recebe mensagens de blocos de mensagens A e B. Se a junção gananciosa receber duas mensagens de B, mas ainda não tiver recebido uma mensagem de A, a junção gananciosa salvará o identificador de mensagem exclusivo para a segunda mensagem de B. Depois que a junção gananciosa recebe uma mensagem de A e propaga essas mensagens, ela usa o identificador de mensagem salva para ver se a segunda mensagem de B ainda está disponível.
Você pode usar a reserva de mensagens ao implementar seus próprios tipos de bloco de mensagens personalizados. Para obter um exemplo sobre como criar um tipo de bloco de mensagem personalizado, consulte Passo a passo: Criando um bloco de mensagem personalizado.
[Topo]