Cenni preliminari sul runtime di concorrenza

In questo documento vengono forniti i cenni preliminari sul runtime di concorrenza. Vengono illustrati i vantaggi del runtime di concorrenza, quando utilizzarlo come e i relativi componenti interagiscono tra loro e con il sistema operativo e le applicazioni.

Sezioni

Il documento include le sezioni seguenti:

  • Importanza del runtime di concorrenza

  • Architettura

  • Espressioni lambda C++

  • Requisiti

Importanza del runtime di concorrenza

Un runtime di concorrenza fornisce uniformità e prevedibilità alle applicazioni e ai componenti dell'applicazione eseguiti simultaneamente. Pianificazione cooperativa delle attività e blocco cooperativo rappresentano due esempi dei vantaggi del runtime di concorrenza.

Il runtime di concorrenza utilizza un'Utilità di pianificazione cooperativa che implementa un algoritmo di acquisizione del lavoro per distribuire il lavoro tra le risorse di elaborazione in modo efficiente. Considerare, ad esempio, un'applicazione che dispone di due thread entrambi gestiti dallo stesso runtime. Se un thread termina l'attività pianificata, può scaricare il lavoro dall'altro thread. Questo meccanismo bilancia il carico di lavoro complessivo dell'applicazione.

Il runtime di concorrenza fornisce inoltre le primitive di sincronizzazione che utilizzano il blocco cooperativo per sincronizzare l'accesso alle risorse. Considerare, ad esempio, un'attività che deve avere l'accesso esclusivo a una risorsa condivisa. Mediante il blocco cooperativo, il runtime può utilizzare il quantum rimanente per eseguire un'altra attività mentre la prima attività è in attesa della risorsa. Questo meccanismo consente il massimo utilizzo delle risorse di elaborazione.

[vai all'inizio]

Architettura

Il runtime di concorrenza è diviso in quattro componenti: libreria PPL (Parallel Patterns Library), libreria di agenti asincroni, Utilità di pianificazione e Gestione risorse. Questi componenti risiedono tra il sistema operativo e le applicazioni. L'illustrazione seguente mostra come i componenti del runtime di concorrenza interagiscono tra il sistema operativo e le applicazioni:

Architettura del runtime di concorrenza

Architettura del runtime di concorrenza

Il runtime di concorrenza è altamente componibile, ovvero è possibile combinare le funzionalità esistenti per ottenere maggiori prestazioni. Il runtime di concorrenza compone molte funzionalità, ad esempio gli algoritmi paralleli, dai componenti di livello inferiore.

Il runtime di concorrenza fornisce inoltre le primitive di sincronizzazione che utilizzano il blocco cooperativo per sincronizzare l'accesso alle risorse. Per ulteriori informazioni sulle primitive di sincronizzazione, vedere Strutture di dati di sincronizzazione.

Nelle sezioni seguenti vengono forniti alcuni cenni preliminari sui vantaggi di ogni componente e sul relativo utilizzo.

PPL (Parallel Patterns Library)

La libreria (Parallel Patterns Library) fornisce contenitori e algoritmi di utilizzo generale per l'esecuzione di un parallelismo accurato. La libreria PPL consente il parallelismo imperativo dei dati fornendo gli algoritmi paralleli che distribuiscono i calcoli sulle raccolte o sui set di dati tra le risorse di elaborazione. Consente inoltre il parallelismo delle attività fornendo oggetti attività che distribuiscono più operazioni indipendenti tra le risorse di elaborazione.

Utilizzare la libreria PPL quando si dispone di un calcolo locale che può trarre vantaggio dall'esecuzione parallela. È possibile, ad esempio, utilizzare l'algoritmo Concurrency::parallel_for per trasformare un ciclo for esistente in modo da agire in parallelo.

Per ulteriori informazioni sulla libreria PPL, vedere PPL (Parallel Patterns Library).

Libreria di agenti asincroni

La libreria di agenti asincroni, o semplicemente libreria di agenti, fornisce un modello di programmazione basato su attori e le interfacce per il passaggio dei messaggi per le attività di pipelining o per un flusso di dati meno accurato. Gli agenti asincroni consentono di utilizzare la latenza in modo produttivo eseguendo il lavoro come gli altri componenti in attesa dei dati.

Utilizzare la libreria di agenti quando si dispone di più entità che comunicano tra loro in modo asincrono. È possibile, ad esempio, creare un agente che legge i dati da un file o da una connessione di rete e quindi utilizza le interfacce per il passaggio dei messaggi per inviare i dati a un altro agente.

Per ulteriori informazioni sulla libreria di agenti, vedere Libreria di agenti asincroni.

Utilità di pianificazione

L'Utilità di pianificazione pianifica e coordina le attività in fase di esecuzione. L'Utilità di pianificazione è cooperativa e utilizza un algoritmo di acquisizione del lavoro per ottenere il massimo utilizzo delle risorse di elaborazione.

Il runtime di concorrenza fornisce un'utilità di pianificazione predefinita in modo da evitare di dover gestire i dettagli dell'infrastruttura. Tuttavia, per soddisfare le esigenze di qualità dell'applicazione, è inoltre possibile fornire criteri di pianificazione personalizzati o associare utilità di pianificazione specifiche a specifiche attività.

Per ulteriori informazioni sull'Utilità di pianificazione, vedere Utilità di pianificazione (runtime di concorrenza).

Gestione risorse

Il ruolo di Gestione risorse è gestire le risorse di elaborazione, ad esempio processori e memoria. Gestione risorse risponde ai carichi di lavoro man mano che vengono modificati in fase di esecuzione assegnando le risorse dove possono risultare più efficaci.

Gestione risorse funge da astrazione sulle risorse di elaborazione e interagisce principalmente con l'Utilità di pianificazione. Sebbene sia possibile utilizzare Gestione risorse per ottimizzare le prestazioni delle librerie e delle applicazioni, in genere vengono utilizzate le funzionalità fornite dalla libreria PPL, dalla libreria di agenti e dall'Utilità di pianificazione. Tali librerie utilizzano Gestione risorse per ribilanciare dinamicamente le risorse in base alla modifica dei carichi di lavoro.

[vai all'inizio]

Espressioni lambda C++

Molti dei tipi e degli algoritmi definiti dal runtime di concorrenza vengono implementati come modelli C++. Alcuni di questi tipi e algoritmi accettano come parametro una routine che esegue il lavoro. Questo parametro può essere una funzione lambda, un oggetto funzione o un puntatore a funzione. Queste entità vengono definite anche funzioni lavoro o routine lavoro.

Le espressioni lambda sono una nuova importante funzionalità del linguaggio di Visual C++ in quanto forniscono una modalità succinta per definire le funzioni lavoro per l'elaborazione in parallelo. Gli oggetti funzione e i puntatori a funzione consentono di utilizzare il runtime di concorrenza con il codice esistente. Tuttavia, è consigliabile utilizzare le espressioni lambda quando si scrive nuovo codice per i vantaggi offerti nella produttività e nella sicurezza.

Nell'esempio seguente vengono confrontati la sintassi delle funzioni lambda, gli oggetti funzione e i puntatori a funzione in più chiamate all'algoritmo Concurrency::parallel_for_each. Ogni chiamata a parallel_for_each utilizza una tecnica diversa per calcolare il quadrato di ogni elemento in un oggetto 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(values.begin(), values.end(), [](int& n){n *= n;});

   // Use a function object (functor) to square each element.
   parallel_for_each(values.begin(), values.end(), SquareFunctor<int>());

   // Use a function pointer to square each element.
   parallel_for_each(values.begin(), values.end(), &square_function<int>);

   // Print each element of the array to the console.
   for_each(values.begin(), values.end(), [](int& n) { 
      wcout << n << endl;
   });
}

Questo esempio produce l'output che segue.

1
256
6561
65536
390625

Per ulteriori informazioni sulle funzioni lambda in C++, vedere Lambda Expressions in C++.

[vai all'inizio]

Requisiti

Nella tabella seguente vengono mostrati i file di intestazione associati a ogni componente del runtime di concorrenza:

Componente

File di intestazione

PPL (Parallel Patterns Library)

ppl.h

concurrent_queue.h

concurrent_vector.h

Libreria di agenti asincroni

agents.h

Utilità di pianificazione

concrt.h

Gestione risorse

concrtrm.h

Il runtime di concorrenza viene dichiarato nello spazio dei nomi Concurrency. Lo spazio dei nomi Concurrency::details supporta il framework del runtime di concorrenza e non deve essere utilizzato direttamente dal codice.

Il runtime di concorrenza viene fornito come parte della Libreria di runtime C (CRT). Per ulteriori informazioni sulla compilazione di un'applicazione che utilizza CRT, vedere C Run-Time Libraries.

[vai all'inizio]

Cronologia delle modifiche

Data

Cronologia

Motivo

Luglio 2010

Il contenuto è stato riorganizzato.

Miglioramento delle informazioni.