Parallelismo fra attività (Task Parallel Library)
La libreria Task Parallel Library (TPL) si basa sul concetto di attività. L'espressione parallelismo delle attività fa riferimento a una o più attività indipendenti eseguite contemporaneamente. Un'attività rappresenta un'operazione asincrona e in qualche modo assomiglia alla creazione di un nuovo thread o di un nuovo elemento di lavoro ThreadPool, ma a un livello di astrazione più elevato. Le attività forniscono due vantaggi principali:
Utilizzo più efficiente e scalabile delle risorse di sistema.
Dietro le quinte, le attività vengono messe in coda a ThreadPool, che è stato migliorato con algoritmi (ad esempio hill-climbing) che determinano il numero di thread che ottimizza la velocità effettiva e si adattano ad esso. In questo modo le attività diventano relativamente leggere ed è possibile crearne molte in modo da ottenere un parallelismo accurato. Per completare questa funzionalità, il sistema utilizza algoritmi noti di acquisizione del lavoro assegnato ad altre risorse allo scopo di garantire il bilanciamento del carico.
Maggior controllo a livello di codice rispetto a quello ottenibile utilizzando un thread o un elemento di lavoro.
Le attività e il framework in cui esse sono inserite forniscono un ampio set di API che supportano varie funzionalità tra cui attesa, annullamento, continuazioni, gestione affidabile delle eccezioni, stato dettagliato e pianificazione personalizzata.
Per entrambi questi motivi, in .NET Framework 4 le attività rappresentano l'API preferita per la scrittura di codice multithreading, asincrono e parallelo.
Creazione ed esecuzione implicite di attività
Il metodo Parallel.Invoke rappresenta un modo pratico per eseguire simultaneamente un numero qualsiasi di istruzioni arbitrarie. Basta passare un delegato Action per ogni elemento di lavoro. Il modo più semplice per creare questi delegati è utilizzare le espressioni lambda. L'espressione lambda può chiamare un metodo denominato o fornire il codice inline. Nell'esempio seguente viene mostrata una chiamata a Invoke di base che crea e avvia due attività in esecuzione simultanea.
Nota |
---|
Questa documentazione utilizza espressioni lambda per definire delegati in TPL.Se non si ha familiarità con le espressioni lambda in C# o Visual Basic, vedere Espressioni lambda in PLINQ e TPL. |
Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())
Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
Nota |
---|
Il numero di istanze Task create automaticamente da Invoke non è necessariamente uguale al numero dei delegati forniti.La libreria TPL può applicare varie ottimizzazioni, specialmente nel caso di un numero elevato di delegati. |
Per ulteriori informazioni, vedere Procedura: utilizzare Parallel.Invoke per eseguire operazioni in parallelo.
Per ottenere un controllo maggiore sull'esecuzione delle attività o per restituire un valore dall'attività, è necessario utilizzare in modo più esplicito gli oggetti Task.
Creazione ed esecuzione esplicite di attività
Un'attività è rappresentata dalla classe System.Threading.Tasks.Task. Un'attività che restituisce un valore viene rappresentata dalla classe System.Threading.Tasks.Task<TResult> che eredita da Task. L'oggetto attività gestisce i dettagli dell'infrastruttura e fornisce metodi e proprietà accessibili dal thread chiamante per tutta la durata dell'attività. Ad esempio, è possibile accedere in qualsiasi momento alla proprietà Status di un'attività per determinare se è stata avviata, eseguita fino al completamento o annullata oppure se ha generato un'eccezione. Lo stato è rappresentato da un'enumerazione TaskStatus.
Quando si crea un'attività si assegna ad essa un delegato dell'utente che incapsula il codice che verrà eseguito dall'attività. Il delegato può essere espresso come un delegato denominato, un metodo anonimo o un'espressione lambda. Le espressioni lambda possono contenere una chiamata a un metodo denominato, come mostrato nell'esempio seguente.
' Create a task and supply a user delegate by using a lambda expression.
Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))
' Start the task.
taskA.Start()
' Output a message from the joining thread.
Console.WriteLine("Hello from the joining thread.")
' Output:
' Hello from the joining thread.
' Hello from taskA.
// Create a task and supply a user delegate by using a lambda expression.
var taskA = new Task(() => Console.WriteLine("Hello from taskA."));
// Start the task.
taskA.Start();
// Output a message from the joining thread.
Console.WriteLine("Hello from the calling thread.");
/* Output:
* Hello from the joining thread.
* Hello from taskA.
*/
È inoltre possibile utilizzare il metodo StartNew per creare e avviare un'attività in un'unica operazione. Questo è il modo preferito per creare e avviare le attività se non è necessario che le fasi di creazione e la pianificazione siano distinte, come illustrato nell'esempio che segue.
' Better: Create and start the task in one operation.
Dim taskA = Task.Factory.StartNew(Sub() Console.WriteLine("Hello from taskA."))
' Output a message from the joining thread.
Console.WriteLine("Hello from the joining thread.")
// Create and start the task in one operation.
var taskA = Task.Factory.StartNew(() => Console.WriteLine("Hello from taskA."));
// Output a message from the joining thread.
Console.WriteLine("Hello from the joining thread.");
L'attività espone una proprietà statica Factory che restituisce un'istanza predefinita di TaskFactory che consente di chiamare il metodo come Task.Factory.StartNew(…). In questo esempio, inoltre, poiché le attività sono di tipo System.Threading.Tasks.Task<TResult>, ognuna di esse presenta una proprietà Result pubblica che contiene il risultato del calcolo. Le attività vengono eseguite in modo asincrono e possono essere completate in qualsiasi ordine. Se si accede a Result prima che il calcolo venga completato, la proprietà bloccherà il thread finché il valore non sarà disponibile.
Dim taskArray() = {Task(Of Double).Factory.StartNew(Function() DoComputation1()),
Task(Of Double).Factory.StartNew(Function() DoComputation2()),
Task(Of Double).Factory.StartNew(Function() DoComputation3())}
Dim results() As Double
ReDim results(taskArray.Length)
For i As Integer = 0 To taskArray.Length
results(i) = taskArray(i).Result
Next
Task<double>[] taskArray = new Task<double>[]
{
Task<double>.Factory.StartNew(() => DoComputation1()),
// May be written more conveniently like this:
Task.Factory.StartNew(() => DoComputation2()),
Task.Factory.StartNew(() => DoComputation3())
};
double[] results = new double[taskArray.Length];
for (int i = 0; i < taskArray.Length; i++)
results[i] = taskArray[i].Result;
Per ulteriori informazioni, vedere Procedura: restituire un valore da un'attività.
Quando si utilizza un'espressione lambda per creare il delegato di un'attività, si avrà accesso a tutte le variabili visibili in quel punto del codice sorgente. Tuttavia, in alcuni casi, in particolare all'interno dei cicli, un'espressione lambda non acquisisce la variabile come previsto. Acquisisce solo il valore finale, non il valore mentre viene modificato dopo ogni iterazione. È possibile accedere al valore in ogni iterazione fornendo un oggetto stato a un'attività mediante il relativo costruttore, come illustrato nell'esempio seguente:
Class MyCustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Sub TaskDemo2()
' Create the task object by using an Action(Of Object) to pass in custom data
' in the Task constructor. This is useful when you need to capture outer variables
' from within a loop.
' As an experiement, try modifying this code to capture i directly in the lamda,
' and compare results.
Dim taskArray() As Task
ReDim taskArray(10)
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = New Task(Sub(obj As Object)
Dim mydata = CType(obj, MyCustomData)
mydata.ThreadNum = Thread.CurrentThread.ManagedThreadId
Console.WriteLine("Hello from Task #{0} created at {1} running on thread #{2}.",
mydata.Name, mydata.CreationTime, mydata.ThreadNum)
End Sub,
New MyCustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks}
)
taskArray(i).Start()
Next
End Sub
class MyCustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
void TaskDemo2()
{
// Create the task object by using an Action(Of Object) to pass in custom data
// in the Task constructor. This is useful when you need to capture outer variables
// from within a loop. As an experiement, try modifying this code to
// capture i directly in the lambda, and compare results.
Task[] taskArray = new Task[10];
for(int i = 0; i < taskArray.Length; i++)
{
taskArray[i] = new Task((obj) =>
{
MyCustomData mydata = (MyCustomData) obj;
mydata.ThreadNum = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Hello from Task #{0} created at {1} running on thread #{2}.",
mydata.Name, mydata.CreationTime, mydata.ThreadNum)
},
new MyCustomData () {Name = i, CreationTime = DateTime.Now.Ticks}
);
taskArray[i].Start();
}
}
Questo stato viene passato come argomento al delegato dell'attività ed è accessibile dall'oggetto attività tramite la proprietà AsyncState. È inoltre possibile che il passaggio dei dati tramite il costruttore produca un piccolo miglioramento delle prestazioni in alcuni scenari.
ID attività
Ogni attività riceve un ID di tipo Integer con cui viene identificata in modo univoco in un dominio applicazione e a cui è possibile accedere tramite la proprietà Id. Questo ID è utile per visualizzare informazioni sull'attività nelle finestre Stack in parallelo e Attività in parallelo del debugger di Visual Studio. L'ID viene creato in modo differito, ovvero solo quando viene richiesto. Pertanto, un'attività può presentare un ID diverso ogni volta che viene eseguito il programma. Per ulteriori informazioni sulla visualizzazione degli ID attività nel debugger, vedere Utilizzo della finestra Stack in parallelo.
Opzioni di creazione delle attività
La maggior parte delle API che creano attività genera overload che accettano un parametro TaskCreationOptions. Quando si specifica una di queste opzioni si indica all'utilità di pianificazione il modo in cui pianificare l'attività nel pool di thread. Nella tabella seguente sono elencate le varie opzioni di creazione delle attività.
Elemento |
Descrizione |
---|---|
None |
Impostazione predefinita quando non si specificano opzioni. L'utilità di pianificazione utilizza le proprie regole euristiche predefinite per pianificare l'attività. |
PreferFairness |
Specifica che l'attività deve essere pianificata in modo che le attività create prima abbiano più possibilità di essere eseguite prima delle attività create in un secondo momento. |
LongRunning |
Specifica che l'attività rappresenta un'operazione di lunga durata. |
AttachedToParent |
Specifica che un'attività deve essere creata come figlio connesso dell'attività corrente, se esiste. Per ulteriori informazioni, vedere Attività annidate e attività figlio. |
Le opzioni possono essere combinate utilizzando un'operazione OR bit per bit. Nell'esempio seguente viene illustrata un'attività che presenta le opzioni LongRunning e PreferFairness.
Dim task3 = New Task(Sub() MyLongRunningMethod(),
TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
task3.Start()
var task3 = new Task(() => MyLongRunningMethod(),
TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();
Creazione delle continuazioni di attività
Il metodo Task.ContinueWith e il metodo Task<TResult>.ContinueWith consentono di specificare un'attività da avviare quando l'attività precedente viene completata. Al delegato dell'attività di continuazione viene passato un riferimento all'attività precedente. In questo modo tale delegato è in grado di esaminare lo stato dell'attività precedente. Inoltre, un valore definito dall'utente può essere passato dall'attività precedente alla relativa continuazione nella proprietà Result. In questo modo, l'output dell'attività precedente può servire da input della continuazione. Nell'esempio seguente l'attività getData viene avviata dal codice del programma. Quindi, l'attività analyzeData viene avviata automaticamente quando getData viene completata e l'attività reportData viene avviata quando analyzeData viene completata. Il risultato prodotto dall'attività getData è una matrice di byte, che viene passata ad analyzeData. L'attività analyzeData elabora tale matrice e restituisce un risultato il cui tipo viene derivato dal tipo restituito del metodo Analyze. L'attività reportData accetta l'input fornito da analyzeData e produce un risultato il cui tipo viene derivato in modo simile e che viene reso disponibile al programma nella proprietà Result.
Dim getData As Task(Of Byte()) = New Task(Of Byte())(Function() GetFileData())
Dim analyzeData As Task(Of Double()) = getData.ContinueWith(Function(x) Analyze(x.Result))
Dim reportData As Task(Of String) = analyzeData.ContinueWith(Function(y As Task(Of Double)) Summarize(y.Result))
getData.Start()
System.IO.File.WriteAllText("C:\reportFolder\report.txt", reportData.Result)
Task<byte[]> getData = new Task<byte[]>(() => GetFileData());
Task<double[]> analyzeData = getData.ContinueWith(x => Analyze(x.Result));
Task<string> reportData = analyzeData.ContinueWith(y => Summarize(y.Result));
getData.Start();
//or...
Task<string> reportData2 = Task.Factory.StartNew(() => GetFileData())
.ContinueWith((x) => Analyze(x.Result))
.ContinueWith((y) => Summarize(y.Result));
System.IO.File.WriteAllText(@"C:\reportFolder\report.txt", reportData.Result);
I metodi ContinueWhenAll e ContinueWhenAny consentono di continuare da più attività. Per ulteriori informazioni, vedere Attività di continuazione e Procedura: concatenare più attività con continuazioni.
Creazione di attività annidate disconnesse
Quando il codice utente in esecuzione in un'attività crea una nuova attività e non specifica l'opzione AttachedToParent, la nuova attività non viene sincronizzata con l'attività esterna in modo particolare. Tali attività vengono definite attività annidate disconnesse. Nell'esempio seguente viene mostrata un'attività che crea un'unica attività annidata disconnessa.
Dim outer = Task.Factory.StartNew(Sub()
Console.WriteLine("Outer task beginning.")
Dim child = Task.Factory.StartNew(Sub()
Thread.SpinWait(5000000)
Console.WriteLine("Detached task completed.")
End Sub)
End Sub)
outer.Wait()
Console.WriteLine("Outer task completed.")
' Output:
' Outer task beginning.
' Outer task completed.
' Detached child completed.
var outer = Task.Factory.StartNew(() =>
{
Console.WriteLine("Outer task beginning.");
var child = Task.Factory.StartNew(() =>
{
Thread.SpinWait(5000000);
Console.WriteLine("Detached task completed.");
});
});
outer.Wait();
Console.WriteLine("Outer task completed.");
/* Output:
Outer task beginning.
Outer task completed.
Detached task completed.
*/
Notare che l'attività esterna non attende il completamento dell'attività annidata.
Creazione di attività figlio
Quando il codice utente in esecuzione in un'attività crea un'attività con l'opzione AttachedToParent, la nuova attività è detta attività figlio dell'attività di origine, definita a sua volta attività padre. È possibile utilizzare l'opzione AttachedToParent per esprimere il parallelismo fra attività strutturato, poiché l'attività padre attende in modo implicito il completamento di tutte le attività figlio. Nell'esempio seguente viene mostrata un'attività che crea un'unica attività figlio:
Dim parent = Task.Factory.StartNew(Sub()
Console.WriteLine("Parent task beginning.")
Dim child = Task.Factory.StartNew(Sub()
Thread.SpinWait(5000000)
Console.WriteLine("Attached child completed.")
End Sub,
TaskCreationOptions.AttachedToParent)
End Sub)
outer.Wait()
Console.WriteLine("Parent task completed.")
' Output:
' Parent task beginning.
' Attached child completed.
' Parent task completed.
var parent = Task.Factory.StartNew(() =>
{
Console.WriteLine("Parent task beginning.");
var child = Task.Factory.StartNew(() =>
{
Thread.SpinWait(5000000);
Console.WriteLine("Attached child completed.");
}, TaskCreationOptions.AttachedToParent);
});
parent.Wait();
Console.WriteLine("Parent task completed.");
/* Output:
Parent task beginning.
Attached task completed.
Parent task completed.
*/
Per ulteriori informazioni, vedere Attività annidate e attività figlio.
Attesa del completamento di una o più attività
I tipi System.Threading.Tasks.Task e System.Threading.Tasks.Task<TResult> forniscono vari overload dei metodi Task.Wait e Task<TResult>.Wait che consentono di attendere il completamento di un'attività. Sono inoltre disponibili overload del metodo Task.WaitAny e del metodo Task.WaitAll statico che consentono rispettivamente di attendere il completamento di alcune o di tutte le attività di una matrice.
L'attesa del completamento di un'attività è in genere dovuta a uno dei motivi seguenti:
Il thread principale dipende dal risultato finale calcolato da un'attività.
È necessario gestire le eccezioni eventualmente generate dall'attività.
Nell'esempio seguente viene mostrato il modello di base che non prevede la gestione delle eccezioni.
Dim tasks() =
{
Task.Factory.StartNew(Sub() MethodA()),
Task.Factory.StartNew(Sub() MethodB()),
Task.Factory.StartNew(Sub() MethodC())
}
' Block until all tasks complete.
Task.WaitAll(tasks)
' Continue on this thread...
Task[] tasks = new Task[3]
{
Task.Factory.StartNew(() => MethodA()),
Task.Factory.StartNew(() => MethodB()),
Task.Factory.StartNew(() => MethodC())
};
//Block until all tasks complete.
Task.WaitAll(tasks);
// Continue on this thread...
Per un esempio che illustra la gestione delle eccezioni, vedere Procedura: gestire le eccezioni generate dalle attività.
Alcuni overload consentono di specificare un timeout mentre altri accettano un oggetto CancellationToken aggiuntivo come parametro di input, in modo che l'attesa stessa possa essere annullata a livello di codice o in risposta a un input dell'utente.
Quando si resta in attesa di un'attività, si attendono in modo implicito tutti i figli di tale attività creati tramite l'opzione AttachedToParent di TaskCreationOptions. L'oggetto Task.Wait restituisce immediatamente un valore se l'attività è già stata completata. Qualsiasi eccezione generata da un'attività verrà generata da un metodo Wait, anche se il metodo Wait è stato chiamato dopo il completamento dell'attività.
Per ulteriori informazioni, vedere Procedura: restare in attesa del completamento di una o più attività.
Gestione delle eccezioni nelle attività
Quando un'attività genera una o più eccezioni, il sistema ne esegue il wrapping in un oggetto AggregateException. Tale eccezione viene ripropagata al thread che si unisce all'attività, che in genere è il thread che resta in attesa dell'attività o che tenta di accedere alla proprietà Result dell'attività. Questo comportamento serve a imporre i criteri di .NET Framework secondo cui tutte le eccezioni non gestite devono comportare per impostazione predefinita l'eliminazione del processo. Il codice che effettua la chiamata può gestire le eccezioni utilizzando il metodo Wait, WaitAll o WaitAny oppure la proprietà Result() nell'attività o nel gruppo di attività e includendo il metodo Wait in un blocco try-catch.
Anche il thread di unione può gestire le eccezioni. A tale scopo, deve accedere alla proprietà Exception prima che l'attività venga raccolta nel Garbage Collector. L'accesso a questa proprietà impedisce all'eccezione non gestita di attivare il comportamento di propagazione delle eccezioni che elimina il processo quando l'oggetto viene completato.
Per ulteriori informazioni sulle eccezioni e sulle attività, vedere Gestione delle eccezioni (Task Parallel Library) e Procedura: gestire le eccezioni generate dalle attività.
Annullamento delle attività
La classe Task supporta l'annullamento cooperativo ed è completamente integrata con la classe System.Threading.CancellationTokenSource e la classe System.Threading.CancellationToken, che rappresentano elementi nuovi della versione 4 di .NET Framework. Molti dei costruttori nella classe System.Threading.Tasks.Task accettano un oggetto CancellationToken come parametro di input. Molti degli overload di StartNew accettano inoltre un oggetto CancellationToken.
È possibile creare il token e inviare la richiesta di annullamento in un secondo momento tramite la classe CancellationTokenSource. Passare il token a Task come argomento. Inoltre, fare riferimento allo stesso token nel delegato dell'utente, che esegue le operazioni necessarie per rispondere a una richiesta di annullamento. Per ulteriori informazioni, vedere Annullamento delle attività e Procedura: annullare un'attività e i relativi figli.
Classe TaskFactory
La classe TaskFactory fornisce metodi statici che incapsulano alcuni modelli comuni per la creazione e l'avvio di attività e di attività di continuazione.
Il modello più comune è StartNew, che crea e avvia un'attività in un'unica istruzione. Per ulteriori informazioni, vedere StartNew().
Quando si creano attività di continuazione da più attività precedenti, utilizzare il metodo ContinueWhenAll oContinueWhenAnyoppure i relativi equivalenti nella classe Task<TResult>. Per ulteriori informazioni, vedere Attività di continuazione.
Per incapsulare i metodi BeginX ed EndX del modello di programmazione asincrono in un'istanza di Task o Task<TResult>, utilizzare i metodi FromAsync. Per ulteriori informazioni, vedere Task Parallel Library e programmazione asincrona .NET tradizionale.
L'oggetto TaskFactory predefinito è accessibile come proprietà statica della classe Task o della classe Task<TResult>. È inoltre possibile creare direttamente un'istanza di TaskFactory e specificare varie opzioni che includono un oggetto CancellationToken, un'opzione TaskCreationOptions, un'opzione TaskContinuationOptions o un oggetto TaskScheduler. Tutte le opzioni specificate quando si crea la factory delle attività verranno applicate a tutte le attività create da tale factory, tranne nel caso in cui l'attività venga creata tramite l'enumerazione TaskCreationOptions. In tal caso, le opzioni dell'attività eseguono l'override di quelle della factory delle attività.
Attività senza delegati
In alcuni casi è necessario utilizzare un oggetto Task per incapsulare un'operazione asincrona eseguita da un componente esterno anziché dal delegato dell'utente. Se l'operazione si basa sul modello Begin/End del modello di programmazione asincrona, è possibile utilizzare i metodi FromAsync. In caso contrario, è possibile utilizzare l'oggetto TaskCompletionSource<TResult> per eseguire il wrapping dell'operazione in un'attività e in questo modo ottenere alcuni dei vantaggi della programmabilità di Task, ad esempio il supporto per la propagazione delle eccezioni e le continuazioni. Per ulteriori informazioni, vedere TaskCompletionSource<TResult>.
Utilità di pianificazione personalizzate
Per la maggior parte degli sviluppatori di applicazioni o librerie non occorre determinare il processore in cui è in esecuzione l'attività né come quest'ultima sincronizza il proprio lavoro con le altre attività o come viene pianificata in System.Threading.ThreadPool. Tali sviluppatori richiedono soltanto che l'attività venga eseguita con la maggiore efficienza possibile nel computer host. Se si richiede un controllo più accurato sui dettagli di pianificazione, la libreria Task Parallel Library (TPL) consente di configurare alcune impostazioni nell'utilità di pianificazione delle attività predefinita. Inoltre, tale libreria consente persino di fornire un'utilità di pianificazione personalizzata. Per ulteriori informazioni, vedere TaskScheduler.
Strutture di dati correlate
La libreria TPL presenta vari nuovi tipi pubblici che risultano utili sia negli scenari in parallelo sia in quelli sequenziali. Fra questi sono incluse varie classi di insiemi thread-safe, veloci e scalabili nello spazio dei nomi System.Collections.Concurrent nonché molti nuovi tipi di sincronizzazione, ad esempio SemaphoreLock e System.Threading.ManualResetEventSlim che sono più efficienti dei relativi predecessori per tipi specifici di carichi di lavoro. Altri nuovi tipi della versione 4 di .NET Framework, ad esempio System.Threading.Barrier e System.Threading.SpinLock, forniscono funzionalità non disponibili nelle versioni precedenti. Per ulteriori informazioni, vedere Strutture di dati per la programmazione in parallelo.
Tipi di attività personalizzati
È consigliabile non ereditare da System.Threading.Tasks.Task o System.Threading.Tasks.Task<TResult>. Utilizzare invece la proprietà AsyncState per associare i dati o lo stato aggiuntivo a un oggetto Task<TResult> o Task. È inoltre possibile utilizzare i metodi di estensione per estendere la funzionalità delle classi Task<TResult> e Task. Per ulteriori informazioni sui metodi di estensione, vedere Metodi di estensione (Guida per programmatori C#) e Metodi di estensione (Visual Basic).
Se è necessario ereditare da Task o da Task<TResult>, non è possibile utilizzare la classe System.Threading.Tasks.TaskFactory System.Threading.Tasks.TaskFactory<TResult> o System.Threading.Tasks.TaskCompletionSource<TResult> per creare istanze del tipo di attività personalizzato, poiché queste classi creano solo oggetti Task<TResult> e Task. Non è inoltre possibile utilizzare i meccanismi di continuazione dell'attività forniti da Task, Task<TResult> TaskFactory e TaskFactory<TResult> per creare istanze del tipo di attività personalizzato poiché anche questi meccanismi creano solo oggetti Task<TResult> e Task.
Argomenti correlati
Titolo |
Descrizione |
Viene descritto il funzionamento delle continuazioni. |
|
Viene descritta la differenza tra attività figlio e attività annidate. |
|
Viene descritto il supporto per l'annullamento compilato nella classe Task. |
|
Viene descritto come vengono gestite le eccezioni su thread simultanei. |
|
Procedura: utilizzare Parallel.Invoke per eseguire operazioni in parallelo |
Viene descritto come utilizzare Invoke. |
Viene descritto come restituire valori dalle attività. |
|
Procedura: restare in attesa del completamento di una o più attività |
Viene descritto come attendere le attività. |
Viene descritto come annullare le attività. |
|
Viene descritto come gestire le eccezioni generate dalle attività. |
|
Viene descritto come eseguire un'attività al completamento di un'altra attività. |
|
Procedura: attraversare un albero binario con attività in parallelo |
Viene descritto come utilizzare le attività per attraversare una struttura ad albero binaria. |
Viene descritto come utilizzare For e ForEach per creare cicli paralleli su dati. |
|
Nodo di livello superiore per la programmazione parallela di .NET. |
Vedere anche
Concetti
Programmazione parallela in .NET Framework
Cronologia delle modifiche
Data |
Cronologia |
Motivo |
---|---|---|
Marzo 2011 |
Informazioni aggiunte su come ereditare dalle classi Task<TResult> e Task. |
Miglioramento delle informazioni. |