Visão geral do Runtime de Simultaneidade
Este documento fornece uma visão geral do Runtime de Simultaneidade. Ele descreve os benefícios do Runtime de Simultaneidade, quando usá-lo e como seus componentes interagem entre si e com o sistema operacional e os aplicativos.
Seções
Este documento contém as seguintes seções:
Histórico de implementação do Runtime de Simultaneidade
No Visual Studio 2010 a 2013, o Runtime de Simultaneidade foi incorporado do msvcr100.dll até o msvcr120.dll. Quando a refatoração UCRT ocorreu no Visual Studio 2015, essa DLL foi refatorada em três partes:
ucrtbase.dll – API C, enviada com o Windows 10 e atualizada em versões inferiores usando o Windows Update
vcruntime140.dll – Funções de suporte do compilador e runtime de EH, enviada por meio do Visual Studio
concrt140.dll – Runtime de Simultaneidade, enviada por meio do Visual Studio. Exigida para contêineres e algoritmos paralelos, como
concurrency::parallel_for
. Além disso, a STL requer que essa DLL no Windows XP dê suporte a primitivas de sincronização, porque o Windows XP não tem variáveis de condição.
No Visual Studio 2015 e posterior, o Agendador de Tarefas do Runtime de Simultaneidade não é mais o agendador para a classe de tarefa e tipos relacionados em ppltasks.h. Esses tipos agora usam o Windows ThreadPool para aprimorar o desempenho e a interoperabilidade com primitivos de sincronização do Windows.
Por que um tempo de execução para simultaneidade é importante
Um runtime para simultaneidade fornece uniformidade e previsibilidade para aplicativos e componentes de aplicativo que são executados simultaneamente. Dois exemplos dos benefícios do Runtime de Simultaneidade são agendamento de tarefas cooperativas e bloqueio cooperativo.
O Runtime de Simultaneidade usa um agendador de tarefas cooperativo que implementa um algoritmo de roubo de trabalho para distribuir com eficiência o trabalho entre os recursos de computação. Por exemplo, considere um aplicativo que tem dois threads que são gerenciados pelo mesmo runtime. Se um thread concluir sua tarefa agendada, ele poderá descarregar o trabalho do outro thread. Esse mecanismo equilibra a carga de trabalho geral do aplicativo.
O Runtime de Simultaneidade também fornece primitivos de sincronização que usam o bloqueio cooperativo para sincronizar o acesso aos recursos. Por exemplo, considere uma tarefa que deve ter acesso exclusivo a um recurso compartilhado. Ao bloquear cooperativamente, o runtime pode usar o quantum restante para executar outra tarefa enquanto a primeira tarefa aguarda o recurso. Esse mecanismo promove o uso máximo de recursos de computação.
Arquitetura
O Runtime de Simultaneidade é dividido em quatro componentes: a PPL (Biblioteca de Padrões Paralelos), a Biblioteca de Agentes Assíncronos, o Agendador de Tarefas e o Resource Manager. Esses componentes residem entre o sistema operacional e os aplicativos. A ilustração a seguir mostra como os componentes do Runtime de Simultaneidade interagem entre o sistema operacional e os aplicativos:
Arquitetura do Runtime de Simultaneidade
Importante
Os componentes do Agendador de Tarefas e do Resource Manager não estão disponíveis em um aplicativo UWP (Plataforma Universal do Windows) ou quando você usa a classe de tarefa ou outros tipos em ppltasks.h.
O Runtime de Simultaneidade é altamente combinável, ou seja, você pode combinar a funcionalidade existente para fazer mais. O Runtime de Simultaneidade compõe muitos recursos, como algoritmos paralelos, de componentes de nível inferior.
O Runtime de Simultaneidade também fornece primitivos de sincronização que usam o bloqueio cooperativo para sincronizar o acesso aos recursos. Para obter mais informações sobre esses primitivos de sincronização, confira Estruturas de Dados de Sincronização.
As seções a seguir fornecem uma breve visão geral do que cada componente fornece e quando usá-lo.
Biblioteca de Padrões Paralelos
A PPL (Biblioteca de Padrões Paralelos) fornece algoritmos e contêineres para fins gerais para executar o paralelismo refinado. A PPL permite o paralelismo de dados imperativos fornecendo algoritmos paralelos que distribuem cálculos em coleções ou em conjuntos de dados entre recursos de computação. Ela também habilita o paralelismo de tarefas fornecendo objetos de tarefa que distribuem várias operações independentes entre recursos de computação.
Use a Biblioteca de Padrões Paralelos quando você tiver uma computação local que possa se beneficiar da execução paralela. Por exemplo, você pode usar o algoritmo concurrency::parallel_for para transformar um loop for
existente para agir em paralelo.
Para obter mais informações sobre a Biblioteca de Padrões Paralelos, consulte PPL (Biblioteca de Padrões Paralelos).
Biblioteca de Agentes Assíncronos
A Biblioteca de Agentes Assíncronos (ou apenas Biblioteca de Agentes) fornece um modelo de programação baseado em ator e interfaces de passagem de mensagem para tarefas de pipeline e de fluxo de dados de alta granularidade. Os agentes assíncronos permitem que você faça uso produtivo da latência executando o trabalho enquanto outros componentes esperam por dados.
Use a Biblioteca de Agentes quando você tiver várias entidades que se comunicam entre si de modo assíncrono. Por exemplo, você pode criar um agente que lê dados de um arquivo ou conexão de rede e usa as interfaces de passagem de mensagem para enviar esses dados para outro agente.
Para mais informações sobre a Biblioteca de Agentes, confira a Biblioteca de Agentes Assíncronos.
Agendador de Tarefas
O Agendador de Tarefas agenda e coordena as tarefas em tempo de execução. O Agendador de Tarefas é cooperativo e usa um algoritmo de roubo de trabalho para obter o uso máximo de recursos de processamento.
O Runtime de Simultaneidade fornece um agendador padrão para que você não precise gerenciar detalhes da infraestrutura. No entanto, para atender às necessidades de qualidade do aplicativo, você também pode fornecer sua própria política de agendamento ou associar agendadores específicos a tarefas específicas.
Para obter mais informações sobre o Agendador de Tarefas, confira Agendador de Tarefas.
Resource Manager
A função do Resource Manager é gerenciar recursos de computação, como processadores e memória. O Resource Manager responde às cargas de trabalho conforme elas são alteradas no tempo de execução, atribuindo recursos onde elas podem ser mais eficazes.
O Resource Manager atua como uma abstração sobre recursos de computação e interage principalmente com o Agendador de Tarefas. Embora você possa usar o Resource Manager para ajustar o desempenho de suas bibliotecas e aplicativos, você normalmente usa a funcionalidade fornecida pela Biblioteca de Padrões Paralelos, pela Biblioteca de Agentes e pelo Agendador de Tarefas. Essas bibliotecas usam o Resource Manager para reequilibrar dinamicamente os recursos à medida que as cargas de trabalho são alteradas.
Expressões do Lambda C++
Muitos dos tipos e algoritmos definidos pelo Runtime de Simultaneidade são implementados como modelos C++. Alguns desses tipos e algoritmos assumem como parâmetro uma rotina que executa o trabalho. Esse parâmetro pode ser uma função lambda, um ponteiro de função ou um objeto de função. Essas entidades também são conhecidas como funções de trabalho ou rotinas de trabalho.
As expressões lambda são um novo recurso de linguagem Visual C++ importante porque fornecem um modo sucinto de definir funções de trabalho para processamento paralelo. Os objetos de função e ponteiros de função permitem que você use o Runtime de Simultaneidade com o código existente. No entanto, recomendamos que você use expressões lambda ao escrever um novo código devido aos benefícios de segurança e produtividade que eles fornecem.
O exemplo a seguir compara a sintaxe de funções lambda, objetos de função e ponteiros de função em várias chamadas ao algoritmo concurrency::parallel_for_each. Cada chamada para parallel_for_each
usar uma técnica diferente para calcular o quadrado de cada elemento em um objeto std::array.
// comparing-work-functions.cpp
// compile with: /EHsc
#include <ppl.h>
#include <array>
#include <iostream>
using namespace concurrency;
using namespace std;
// Function object (functor) class that computes the square of its input.
template<class Ty>
class SquareFunctor
{
public:
void operator()(Ty& n) const
{
n *= n;
}
};
// Function that computes the square of its input.
template<class Ty>
void square_function(Ty& n)
{
n *= n;
}
int wmain()
{
// Create an array object that contains 5 values.
array<int, 5> values = { 1, 2, 3, 4, 5 };
// Use a lambda function, a function object, and a function pointer to
// compute the square of each element of the array in parallel.
// Use a lambda function to square each element.
parallel_for_each(begin(values), end(values), [](int& n){n *= n;});
// Use a function object (functor) to square each element.
parallel_for_each(begin(values), end(values), SquareFunctor<int>());
// Use a function pointer to square each element.
parallel_for_each(begin(values), end(values), &square_function<int>);
// Print each element of the array to the console.
for_each(begin(values), end(values), [](int& n) {
wcout << n << endl;
});
}
Saída
1
256
6561
65536
390625
Para obter mais informações sobre funções lambda no C++, confira Expressões lambda.
Requisitos
A seguinte tabela mostra os arquivos de cabeçalho associados a cada componente do Runtime de Simultaneidade:
Componente | Arquivos de cabeçalho |
---|---|
Biblioteca de padrões paralelos (PPL) | ppl.h concurrent_queue.h concurrent_vector.h |
Biblioteca de Agentes Assíncronos | agents.h |
Agendador de Tarefas | concrt.h |
Resource Manager | concrtrm.h |
O Runtime de Simultaneidade é declarado no namespace Simultaneidade. (Você também pode usar simultaneidade, que é um alias para esse namespace). O namespace concurrency::details
dá suporte à estrutura de Runtime de Simultaneidade e não se destina a ser usado diretamente do seu código.
O Runtime de Simultaneidade é fornecido como parte da CRT (Biblioteca de Runtime C). Para obter mais informações sobre como criar um aplicativo que usa a CRT, confira Recursos da Biblioteca da CRT.