Programmazione asincrona basata su attività
Task Parallel Library (TPL) si basa sul concetto di un'attività , che rappresenta un'operazione asincrona. In alcuni aspetti, un'attività è simile a un thread o a un elemento di lavoro ThreadPool, ma a un livello superiore di astrazione. Il termine parallelismo attività fa riferimento a una o più attività indipendenti in esecuzione simultaneamente. Le attività offrono due vantaggi principali:
Uso più efficiente e più scalabile delle risorse di sistema.
Dietro le quinte, le attività vengono accodate al ThreadPool, che è stato migliorato con algoritmi che determinano e regolano il numero di thread. Questi algoritmi forniscono il bilanciamento del carico per ottimizzare la velocità effettiva. Questo processo rende le attività relativamente leggere ed è possibile crearne molte per abilitare il parallelismo con granularità fine.
Controllo più programmatico di quanto sia possibile con un thread o un elemento di lavoro.
Le attività e il framework basati su di essi offrono un set completo di API che supportano l'attesa, l'annullamento, le continuazioni, la gestione affidabile delle eccezioni, lo stato dettagliato, la pianificazione personalizzata e altro ancora.
Per entrambi i motivi, TPL è l'API preferita per la scrittura di codice multithreading, asincrono e parallelo in .NET.
Creazione ed esecuzione di attività in modo implicito
Il metodo Parallel.Invoke offre un modo pratico per eseguire contemporaneamente un numero qualsiasi di istruzioni arbitrarie. È sufficiente passare un delegato Action per ogni elemento di lavoro. Il modo più semplice per creare questi delegati consiste nell'usare espressioni lambda. L'espressione lambda può chiamare un metodo denominato o fornire il codice inline. Nell'esempio seguente viene illustrata una chiamata di base Invoke che crea e avvia due attività che vengono eseguite contemporaneamente. La prima attività è rappresentata da un'espressione lambda che chiama un metodo denominato DoSomeWork
e la seconda attività è rappresentata da un'espressione lambda che chiama un metodo denominato DoSomeOtherWork
.
Nota
Questa documentazione usa 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(() => DoSomeWork(), () => DoSomeOtherWork());
Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())
Nota
Il numero di istanze di Task create dietro le quinte da Invoke non è necessariamente uguale al numero di delegati forniti. Il TPL potrebbe usare varie ottimizzazioni, in particolare con un numero elevato di delegati.
Per altre informazioni, vedere Procedura: Usare Parallel.Invoke per eseguire operazioni parallele.
Per un maggiore controllo sull'esecuzione dell'attività o sulla restituzione di un valore dall'attività, è necessario usare Task oggetti in modo più esplicito.
Creazione ed esecuzione di attività in modo esplicito
Un'attività che non restituisce un valore è rappresentata dalla classe System.Threading.Tasks.Task. Un'attività che restituisce un valore è 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 alla proprietà Status di un'attività in qualsiasi momento per determinare se è stata avviata l'esecuzione, è stata eseguita fino al completamento, è stata annullata o ha generato un'eccezione. Lo stato è rappresentato da un'enumerazione TaskStatus.
Quando si crea un'attività, si assegna un delegato utente che incapsula il codice che verrà eseguito dall'attività. Il delegato può essere espresso come un delegato denominato, un metodo anonimo o come un'espressione lambda. Le espressioni lambda possono contenere una chiamata a un metodo denominato, come illustrato nell'esempio seguente. L'esempio include una chiamata al metodo Task.Wait per assicurarsi che l'attività venga completata prima che l'applicazione in modalità console termini.
using System;
using System.Threading;
using System.Threading.Tasks;
public class Lambda
{
public static void Main()
{
Thread.CurrentThread.Name = "Main";
// Create a task and supply a user delegate by using a lambda expression.
Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
// Start the task.
taskA.Start();
// Output a message from the calling thread.
Console.WriteLine("Hello from thread '{0}'.",
Thread.CurrentThread.Name);
taskA.Wait();
}
}
// The example displays output as follows:
// Hello from thread 'Main'.
// Hello from taskA.
// or
// Hello from taskA.
// Hello from thread 'Main'.
Imports System.Threading
Namespace Lambda
Module Example
Public Sub Main()
Thread.CurrentThread.Name = "Main"
' 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 calling thread.
Console.WriteLine("Hello from thread '{0}'.",
Thread.CurrentThread.Name)
taskA.Wait()
End Sub
End Module
' The example displays output like the following:
' Hello from thread 'Main'.
' Hello from taskA.
End Namespace
È anche possibile usare i metodi Task.Run per creare e avviare un'attività in un'unica operazione. Per gestire l'attività, i metodi Run usano l'utilità di pianificazione predefinita, indipendentemente dall'utilità di pianificazione dell'attività associata al thread corrente. I metodi Run sono il modo preferito per creare e avviare attività quando non è necessario un maggiore controllo sulla creazione e la pianificazione dell'attività.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Run;
public class Example
{
public static void Main()
{
Thread.CurrentThread.Name = "Main";
// Define and run the task.
Task taskA = Task.Run( () => Console.WriteLine("Hello from taskA."));
// Output a message from the calling thread.
Console.WriteLine("Hello from thread '{0}'.",
Thread.CurrentThread.Name);
taskA.Wait();
}
}
// The example displays output as follows:
// Hello from thread 'Main'.
// Hello from taskA.
// or
// Hello from taskA.
// Hello from thread 'Main'.
Imports System.Threading
Namespace Run
Module Example
Public Sub Main()
Thread.CurrentThread.Name = "Main"
Dim taskA As Task = Task.Run(Sub() Console.WriteLine("Hello from taskA."))
' Output a message from the calling thread.
Console.WriteLine("Hello from thread '{0}'.",
Thread.CurrentThread.Name)
taskA.Wait()
End Sub
End Module
' The example displays output like the following:
' Hello from thread 'Main'.
' Hello from taskA.
End Namespace
È anche possibile usare il metodo TaskFactory.StartNew per creare e avviare un'attività in un'unica operazione. Come illustrato nell'esempio seguente, è possibile usare questo metodo quando:
Non è necessario separare la creazione e la pianificazione e richiedere opzioni aggiuntive per la creazione di attività o l'uso di un'utilità di pianificazione specifica.
È necessario passare uno stato aggiuntivo all'attività che è possibile recuperare tramite la relativa proprietà Task.AsyncState.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskIntro;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
public class AsyncState
{
public static void Main()
{
Task[] taskArray = new Task[10];
for (int i = 0; i < taskArray.Length; i++)
{
taskArray[i] = Task.Factory.StartNew((Object obj) =>
{
CustomData data = obj as CustomData;
if (data == null) return;
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
},
new CustomData() { Name = i, CreationTime = DateTime.Now.Ticks });
}
Task.WaitAll(taskArray);
foreach (var task in taskArray)
{
var data = task.AsyncState as CustomData;
if (data != null)
Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum);
}
}
}
// The example displays output like the following:
// Task #0 created at 635116412924597583, ran on thread #3.
// Task #1 created at 635116412924607584, ran on thread #4.
// Task #2 created at 635116412924607584, ran on thread #4.
// Task #3 created at 635116412924607584, ran on thread #4.
// Task #4 created at 635116412924607584, ran on thread #3.
// Task #5 created at 635116412924607584, ran on thread #3.
// Task #6 created at 635116412924607584, ran on thread #4.
// Task #7 created at 635116412924607584, ran on thread #4.
// Task #8 created at 635116412924607584, ran on thread #3.
// Task #9 created at 635116412924607584, ran on thread #4.
Imports System.Threading
Namespace AsyncState
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As CustomData = TryCast(obj, CustomData)
If data Is Nothing Then Return
data.ThreadNum = Environment.CurrentManagedThreadId
End Sub,
New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
Next
Task.WaitAll(taskArray)
For Each task In taskArray
Dim data = TryCast(task.AsyncState, CustomData)
If data IsNot Nothing Then
Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum)
End If
Next
End Sub
End Module
' The example displays output like the following:
' Task #0 created at 635116412924597583, ran on thread #3.
' Task #1 created at 635116412924607584, ran on thread #4.
' Task #2 created at 635116412924607584, ran on thread #4.
' Task #3 created at 635116412924607584, ran on thread #4.
' Task #4 created at 635116412924607584, ran on thread #3.
' Task #5 created at 635116412924607584, ran on thread #3.
' Task #6 created at 635116412924607584, ran on thread #4.
' Task #7 created at 635116412924607584, ran on thread #4.
' Task #8 created at 635116412924607584, ran on thread #3.
' Task #9 created at 635116412924607584, ran on thread #4.
End Namespace
Task e Task<TResult> ognuno espone una proprietà Factory statica che restituisce un'istanza predefinita di TaskFactory, in modo che sia possibile chiamare il metodo come Task.Factory.StartNew()
. Inoltre, nell'esempio seguente, poiché le attività sono di tipo System.Threading.Tasks.Task<TResult>, ognuna ha una proprietà pubblica Task<TResult>.Result che contiene il risultato del calcolo. Le attività vengono eseguite in modo asincrono e possono essere completate in qualsiasi ordine. Se si accede alla proprietà Result prima del completamento del calcolo, la proprietà blocca il thread chiamante finché il valore non è disponibile.
using System;
using System.Threading.Tasks;
public class Result
{
public static void Main()
{
Task<Double>[] taskArray = { Task<Double>.Factory.StartNew(() => DoComputation(1.0)),
Task<Double>.Factory.StartNew(() => DoComputation(100.0)),
Task<Double>.Factory.StartNew(() => DoComputation(1000.0)) };
var results = new Double[taskArray.Length];
Double sum = 0;
for (int i = 0; i < taskArray.Length; i++) {
results[i] = taskArray[i].Result;
Console.Write("{0:N1} {1}", results[i],
i == taskArray.Length - 1 ? "= " : "+ ");
sum += results[i];
}
Console.WriteLine("{0:N1}", sum);
}
private static Double DoComputation(Double start)
{
Double sum = 0;
for (var value = start; value <= start + 10; value += .1)
sum += value;
return sum;
}
}
// The example displays the following output:
// 606.0 + 10,605.0 + 100,495.0 = 111,706.0
Namespace Result
Module Example
Public Sub Main()
Dim taskArray() = {Task(Of Double).Factory.StartNew(Function() DoComputation(1.0)),
Task(Of Double).Factory.StartNew(Function() DoComputation(100.0)),
Task(Of Double).Factory.StartNew(Function() DoComputation(1000.0))}
Dim results(taskArray.Length - 1) As Double
Dim sum As Double
For i As Integer = 0 To taskArray.Length - 1
results(i) = taskArray(i).Result
Console.Write("{0:N1} {1}", results(i),
If(i = taskArray.Length - 1, "= ", "+ "))
sum += results(i)
Next
Console.WriteLine("{0:N1}", sum)
End Sub
Private Function DoComputation(start As Double) As Double
Dim sum As Double
For value As Double = start To start + 10 Step .1
sum += value
Next
Return sum
End Function
End Module
' The example displays the following output:
' 606.0 + 10,605.0 + 100,495.0 = 111,706.0
End Namespace
Per altre informazioni, vedere Procedura: Restituire un valore da un'attività.
Quando si usa un'espressione lambda per creare un delegato, è possibile accedere a tutte le variabili visibili in quel punto nel codice sorgente. Tuttavia, in alcuni casi, in particolare all'interno dei cicli, un'espressione lambda non acquisisce la variabile come previsto. Acquisisce solo il riferimento della variabile, non il valore, perché modifica dopo ogni iterazione. Nell'esempio seguente viene illustrato il problema. Passa un contatore del ciclo a un'espressione lambda che istanzia l'oggetto CustomData
e utilizza il contatore del ciclo come identificatore dell'oggetto. Come illustrato nell'output dell'esempio, ogni oggetto CustomData
ha un identificatore identico.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Example.Iterations;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
public class IterationTwo
{
public static void Main()
{
// Create the task object by using an Action(Of Object) to pass in the loop
// counter. This produces an unexpected result.
Task[] taskArray = new Task[10];
for (int i = 0; i < taskArray.Length; i++) {
taskArray[i] = Task.Factory.StartNew( (Object obj) => {
var data = new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks};
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum);
},
i );
}
Task.WaitAll(taskArray);
}
}
// The example displays output like the following:
// Task #10 created at 635116418427727841 on thread #4.
// Task #10 created at 635116418427737842 on thread #4.
// Task #10 created at 635116418427737842 on thread #4.
// Task #10 created at 635116418427737842 on thread #4.
// Task #10 created at 635116418427737842 on thread #4.
// Task #10 created at 635116418427737842 on thread #4.
// Task #10 created at 635116418427727841 on thread #3.
// Task #10 created at 635116418427747843 on thread #3.
// Task #10 created at 635116418427747843 on thread #3.
// Task #10 created at 635116418427737842 on thread #4.
Imports System.Threading
Namespace IterationsTwo
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
' Create the task object by using an Action(Of Object) to pass in the loop
' counter. This produces an unexpected result.
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks}
data.ThreadNum = Environment.CurrentManagedThreadId
Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum)
End Sub,
i)
Next
Task.WaitAll(taskArray)
End Sub
End Module
' The example displays output like the following:
' Task #10 created at 635116418427727841 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427727841 on thread #3.
' Task #10 created at 635116418427747843 on thread #3.
' Task #10 created at 635116418427747843 on thread #3.
' Task #10 created at 635116418427737842 on thread #4.
End Namespace
È possibile accedere al valore in ogni iterazione fornendo un oggetto stato a un'attività tramite il relativo costruttore. Nell'esempio seguente viene modificato l'esempio precedente usando il contatore del ciclo durante la creazione dell'oggetto CustomData
, che a sua volta viene passato all'espressione lambda. Come mostrato nell'output dell'esempio, ogni oggetto CustomData
ha ora un identificatore univoco basato sul valore del contatore del ciclo al momento in cui l'oggetto è stato istanziato.
using System;
using System.Threading;
using System.Threading.Tasks;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
public class IterationOne
{
public static void Main()
{
// Create the task object by using an Action(Of Object) to pass in custom data
// to the Task constructor. This is useful when you need to capture outer variables
// from within a loop.
Task[] taskArray = new Task[10];
for (int i = 0; i < taskArray.Length; i++) {
taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
CustomData data = obj as CustomData;
if (data == null)
return;
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum);
},
new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
}
Task.WaitAll(taskArray);
}
}
// The example displays output like the following:
// Task #0 created at 635116412924597583 on thread #3.
// Task #1 created at 635116412924607584 on thread #4.
// Task #3 created at 635116412924607584 on thread #4.
// Task #4 created at 635116412924607584 on thread #4.
// Task #2 created at 635116412924607584 on thread #3.
// Task #6 created at 635116412924607584 on thread #3.
// Task #5 created at 635116412924607584 on thread #4.
// Task #8 created at 635116412924607584 on thread #4.
// Task #7 created at 635116412924607584 on thread #3.
// Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Namespace IterationsOne
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
' Create the task object by using an Action(Of Object) to pass in custom data
' to the Task constructor. This is useful when you need to capture outer variables
' from within a loop.
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As CustomData = TryCast(obj, CustomData)
If data Is Nothing Then Return
data.ThreadNum = Environment.CurrentManagedThreadId
Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum)
End Sub,
New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
Next
Task.WaitAll(taskArray)
End Sub
End Module
' The example displays output like the following:
' Task #0 created at 635116412924597583 on thread #3.
' Task #1 created at 635116412924607584 on thread #4.
' Task #3 created at 635116412924607584 on thread #4.
' Task #4 created at 635116412924607584 on thread #4.
' Task #2 created at 635116412924607584 on thread #3.
' Task #6 created at 635116412924607584 on thread #3.
' Task #5 created at 635116412924607584 on thread #4.
' Task #8 created at 635116412924607584 on thread #4.
' Task #7 created at 635116412924607584 on thread #3.
' Task #9 created at 635116412924607584 on thread #4.
End Namespace
Questo stato viene passato come argomento al delegato dell'attività ed è accessibile dall'oggetto attività usando la proprietà Task.AsyncState. L'esempio seguente è una variante dell'esempio precedente. Usa la proprietà AsyncState per visualizzare informazioni sugli oggetti CustomData
passati all'espressione lambda.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskIntro;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
public class AsyncState
{
public static void Main()
{
Task[] taskArray = new Task[10];
for (int i = 0; i < taskArray.Length; i++)
{
taskArray[i] = Task.Factory.StartNew((Object obj) =>
{
CustomData data = obj as CustomData;
if (data == null) return;
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
},
new CustomData() { Name = i, CreationTime = DateTime.Now.Ticks });
}
Task.WaitAll(taskArray);
foreach (var task in taskArray)
{
var data = task.AsyncState as CustomData;
if (data != null)
Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum);
}
}
}
// The example displays output like the following:
// Task #0 created at 635116412924597583, ran on thread #3.
// Task #1 created at 635116412924607584, ran on thread #4.
// Task #2 created at 635116412924607584, ran on thread #4.
// Task #3 created at 635116412924607584, ran on thread #4.
// Task #4 created at 635116412924607584, ran on thread #3.
// Task #5 created at 635116412924607584, ran on thread #3.
// Task #6 created at 635116412924607584, ran on thread #4.
// Task #7 created at 635116412924607584, ran on thread #4.
// Task #8 created at 635116412924607584, ran on thread #3.
// Task #9 created at 635116412924607584, ran on thread #4.
Imports System.Threading
Namespace AsyncState
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As CustomData = TryCast(obj, CustomData)
If data Is Nothing Then Return
data.ThreadNum = Environment.CurrentManagedThreadId
End Sub,
New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
Next
Task.WaitAll(taskArray)
For Each task In taskArray
Dim data = TryCast(task.AsyncState, CustomData)
If data IsNot Nothing Then
Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum)
End If
Next
End Sub
End Module
' The example displays output like the following:
' Task #0 created at 635116412924597583, ran on thread #3.
' Task #1 created at 635116412924607584, ran on thread #4.
' Task #2 created at 635116412924607584, ran on thread #4.
' Task #3 created at 635116412924607584, ran on thread #4.
' Task #4 created at 635116412924607584, ran on thread #3.
' Task #5 created at 635116412924607584, ran on thread #3.
' Task #6 created at 635116412924607584, ran on thread #4.
' Task #7 created at 635116412924607584, ran on thread #4.
' Task #8 created at 635116412924607584, ran on thread #3.
' Task #9 created at 635116412924607584, ran on thread #4.
End Namespace
ID attività
Ogni attività riceve un ID intero che lo identifica in modo univoco in un dominio applicazione ed è accessibile usando la proprietà Task.Id. L'ID è utile per visualizzare le informazioni sulle attività nelle finestre "Stack Paralleli" e "Attività" del debugger di Visual Studio e . L'ID viene creato in modo pigro, il che significa che non viene creato fino a quando non viene richiesto. Pertanto, un'attività potrebbe avere un ID diverso ogni volta che viene eseguito il programma. Per ulteriori informazioni su come visualizzare gli ID attività nel debugger, vedere Uso della finestra delle attività e Uso della finestra degli stack paralleli.
Le opzioni di creazione delle attività
La maggior parte delle API che creano attività offre sovraccarichi che supportano un parametro TaskCreationOptions. Specificando una o più di queste opzioni, si indica allo scheduler delle attività come programmare l'attività nel pool di thread. Le opzioni possono essere combinate usando un'operazione OR bit per bit.
L'esempio seguente mostra un'attività con le opzioni di LongRunning e PreferFairness:
var task3 = new Task(() => MyLongRunningMethod(),
TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();
Dim task3 = New Task(Sub() MyLongRunningMethod(),
TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
task3.Start()
Attività, thread e impostazioni locali
Ogni thread ha una cultura e una cultura dell'interfaccia utente associate, definite rispettivamente dalle proprietà Thread.CurrentCulture e Thread.CurrentUICulture. Le impostazioni culturali di un thread vengono utilizzate in operazioni come la formattazione, l'analisi, l'ordinamento e il confronto delle stringhe. La cultura dell'interfaccia utente di un thread viene utilizzata per la ricerca delle risorse.
Le impostazioni culturali del sistema definiscono la cultura predefinita e quella dell'interfaccia utente di un thread. Tuttavia, è possibile specificare impostazioni cultura predefinite per tutti i thread in un dominio applicazione usando le proprietà CultureInfo.DefaultThreadCurrentCulture e CultureInfo.DefaultThreadCurrentUICulture. Se si imposta esplicitamente la cultura di un thread e si avvia un nuovo thread, il nuovo thread non eredita la cultura del thread chiamante; invece, la sua cultura è quella predefinita del sistema. Tuttavia, nella programmazione basata su task, i task utilizzano la cultura del thread chiamante, anche se il task viene eseguito in modo asincrono su un thread diverso.
Nell'esempio seguente viene fornita una semplice illustrazione. Cambia la cultura corrente dell'app in francese (Francia). Se la lingua corrente è già impostata su francese (Francia), viene cambiata in inglese (degli Stati Uniti). Richiama quindi un delegato denominato formatDelegate
che restituisce alcuni numeri formattati come valore monetario nel nuovo contesto culturale. Se il delegato viene invocato da un task in modo sincrono o asincrono, il task utilizza le impostazioni culturali del thread chiamante.
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
decimal[] values = { 163025412.32m, 18905365.59m };
string formatString = "C2";
Func<String> formatDelegate = () => { string output = String.Format("Formatting using the {0} culture on thread {1}.\n",
CultureInfo.CurrentCulture.Name,
Thread.CurrentThread.ManagedThreadId);
foreach (var value in values)
output += String.Format("{0} ", value.ToString(formatString));
output += Environment.NewLine;
return output;
};
Console.WriteLine("The example is running on thread {0}",
Thread.CurrentThread.ManagedThreadId);
// Make the current culture different from the system culture.
Console.WriteLine("The current culture is {0}",
CultureInfo.CurrentCulture.Name);
if (CultureInfo.CurrentCulture.Name == "fr-FR")
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
else
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Console.WriteLine("Changed the current culture to {0}.\n",
CultureInfo.CurrentCulture.Name);
// Execute the delegate synchronously.
Console.WriteLine("Executing the delegate synchronously:");
Console.WriteLine(formatDelegate());
// Call an async delegate to format the values using one format string.
Console.WriteLine("Executing a task asynchronously:");
var t1 = Task.Run(formatDelegate);
Console.WriteLine(t1.Result);
Console.WriteLine("Executing a task synchronously:");
var t2 = new Task<String>(formatDelegate);
t2.RunSynchronously();
Console.WriteLine(t2.Result);
}
}
// The example displays the following output:
// The example is running on thread 1
// The current culture is en-US
// Changed the current culture to fr-FR.
//
// Executing the delegate synchronously:
// Formatting using the fr-FR culture on thread 1.
// 163 025 412,32 € 18 905 365,59 €
//
// Executing a task asynchronously:
// Formatting using the fr-FR culture on thread 3.
// 163 025 412,32 € 18 905 365,59 €
//
// Executing a task synchronously:
// Formatting using the fr-FR culture on thread 1.
// 163 025 412,32 € 18 905 365,59 €
Imports System.Globalization
Imports System.Threading
Module Example
Public Sub Main()
Dim values() As Decimal = {163025412.32D, 18905365.59D}
Dim formatString As String = "C2"
Dim formatDelegate As Func(Of String) = Function()
Dim output As String = String.Format("Formatting using the {0} culture on thread {1}.",
CultureInfo.CurrentCulture.Name,
Thread.CurrentThread.ManagedThreadId)
output += Environment.NewLine
For Each value In values
output += String.Format("{0} ", value.ToString(formatString))
Next
output += Environment.NewLine
Return output
End Function
Console.WriteLine("The example is running on thread {0}",
Thread.CurrentThread.ManagedThreadId)
' Make the current culture different from the system culture.
Console.WriteLine("The current culture is {0}",
CultureInfo.CurrentCulture.Name)
If CultureInfo.CurrentCulture.Name = "fr-FR" Then
Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
Else
Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-FR")
End If
Console.WriteLine("Changed the current culture to {0}.",
CultureInfo.CurrentCulture.Name)
Console.WriteLine()
' Execute the delegate synchronously.
Console.WriteLine("Executing the delegate synchronously:")
Console.WriteLine(formatDelegate())
' Call an async delegate to format the values using one format string.
Console.WriteLine("Executing a task asynchronously:")
Dim t1 = Task.Run(formatDelegate)
Console.WriteLine(t1.Result)
Console.WriteLine("Executing a task synchronously:")
Dim t2 = New Task(Of String)(formatDelegate)
t2.RunSynchronously()
Console.WriteLine(t2.Result)
End Sub
End Module
' The example displays the following output:
'
' The example is running on thread 1
' The current culture is en-US
' Changed the current culture to fr-FR.
'
' Executing the delegate synchronously:
' Formatting Imports the fr-FR culture on thread 1.
' 163 025 412,32 € 18 905 365,59 €
'
' Executing a task asynchronously:
' Formatting Imports the fr-FR culture on thread 3.
' 163 025 412,32 € 18 905 365,59 €
'
' Executing a task synchronously:
' Formatting Imports the fr-FR culture on thread 1.
' 163 025 412,32 € 18 905 365,59 €
Nota
Nelle versioni di .NET Framework precedenti a .NET Framework 4.6, la cultura di un task è determinata dalla cultura del thread su cui viene eseguita, non dalla cultura del thread che chiama. Per le attività asincrone, la cultura utilizzata dall'attività potrebbe essere diversa dalla cultura del thread chiamante.
Per ulteriori informazioni sulle attività asincrone e sulla cultura, vedere la sezione "Cultura e operazioni basate su attività asincrone" nell'articolo CultureInfo.
Creazione di continuazioni di attività
I metodi Task.ContinueWith e Task<TResult>.ContinueWith consentono di specificare un'attività da avviare al termine dell'attività precedente . Il delegato dell'attività di continuazione viene passato un riferimento all'attività precedente in modo che possa esaminare lo stato dell'attività precedente. Recuperando il valore della proprietà Task<TResult>.Result, è possibile usare l'output dell'antecedente come input per la continuazione.
Nell'esempio seguente l'attività getData
viene avviata da una chiamata al metodo TaskFactory.StartNew<TResult>(Func<TResult>). L'attività processData
viene avviata automaticamente al termine della getData
e displayData
viene avviata al termine dell'processData
.
getData
produce una matrice integer, accessibile all'attività processData
tramite la proprietà Task<TResult>.Result dell'attività getData
. L'attività processData
elabora la matrice e restituisce un risultato il cui tipo viene dedotto dal tipo restituito dell'espressione lambda passata al metodo Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>). L'attività displayData
viene eseguita automaticamente al termine dell'processData
e l'oggetto Tuple<T1,T2,T3> restituito dall'espressione lambda processData
è accessibile all'attività displayData
tramite la proprietà Task<TResult>.Result dell'attività processData
. L'attività displayData
ottiene il risultato dell'attività processData
. Produce un risultato il cui tipo viene dedotto in modo simile e che viene reso disponibile al programma nella proprietà Result.
using System;
using System.Threading.Tasks;
public class ContinuationOne
{
public static void Main()
{
var getData = Task.Factory.StartNew(() => {
Random rnd = new Random();
int[] values = new int[100];
for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
values[ctr] = rnd.Next();
return values;
} );
var processData = getData.ContinueWith((x) => {
int n = x.Result.Length;
long sum = 0;
double mean;
for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
sum += x.Result[ctr];
mean = sum / (double) n;
return Tuple.Create(n, sum, mean);
} );
var displayData = processData.ContinueWith((x) => {
return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
x.Result.Item1, x.Result.Item2,
x.Result.Item3);
} );
Console.WriteLine(displayData.Result);
}
}
// The example displays output similar to the following:
// N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
Namespace ContinuationsOne
Module Example
Public Sub Main()
Dim getData = Task.Factory.StartNew(Function()
Dim rnd As New Random()
Dim values(99) As Integer
For ctr = 0 To values.GetUpperBound(0)
values(ctr) = rnd.Next()
Next
Return values
End Function)
Dim processData = getData.ContinueWith(Function(x)
Dim n As Integer = x.Result.Length
Dim sum As Long
Dim mean As Double
For ctr = 0 To x.Result.GetUpperBound(0)
sum += x.Result(ctr)
Next
mean = sum / n
Return Tuple.Create(n, sum, mean)
End Function)
Dim displayData = processData.ContinueWith(Function(x)
Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
x.Result.Item1, x.Result.Item2,
x.Result.Item3)
End Function)
Console.WriteLine(displayData.Result)
End Sub
End Module
' The example displays output like the following:
' N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
End Namespace
Poiché Task.ContinueWith è un metodo di istanza, è possibile concatenare le invocazioni dei metodi invece di creare un'istanza di un oggetto Task<TResult> per ogni attività precedente. L'esempio seguente è funzionalmente identico a quello precedente, ad eccezione del fatto che concatena le chiamate al metodo Task.ContinueWith. L'oggetto Task<TResult> restituito dalle chiamate di metodo concatenate è l'attività di continuazione finale.
using System;
using System.Threading.Tasks;
public class ContinuationTwo
{
public static void Main()
{
var displayData = Task.Factory.StartNew(() => {
Random rnd = new Random();
int[] values = new int[100];
for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
values[ctr] = rnd.Next();
return values;
} ).
ContinueWith((x) => {
int n = x.Result.Length;
long sum = 0;
double mean;
for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
sum += x.Result[ctr];
mean = sum / (double) n;
return Tuple.Create(n, sum, mean);
} ).
ContinueWith((x) => {
return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
x.Result.Item1, x.Result.Item2,
x.Result.Item3);
} );
Console.WriteLine(displayData.Result);
}
}
// The example displays output similar to the following:
// N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
Namespace ContinuationsTwo
Module Example
Public Sub Main()
Dim displayData = Task.Factory.StartNew(Function()
Dim rnd As New Random()
Dim values(99) As Integer
For ctr = 0 To values.GetUpperBound(0)
values(ctr) = rnd.Next()
Next
Return values
End Function). _
ContinueWith(Function(x)
Dim n As Integer = x.Result.Length
Dim sum As Long
Dim mean As Double
For ctr = 0 To x.Result.GetUpperBound(0)
sum += x.Result(ctr)
Next
mean = sum / n
Return Tuple.Create(n, sum, mean)
End Function). _
ContinueWith(Function(x)
Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
x.Result.Item1, x.Result.Item2,
x.Result.Item3)
End Function)
Console.WriteLine(displayData.Result)
End Sub
End Module
' The example displays output like the following:
' N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
End Namespace
I metodi ContinueWhenAll e ContinueWhenAny consentono di proseguire da diverse attività.
Per altre informazioni, vedere concatenamento delle attività tramite attività di continuazione.
Creazione di attività figlio scollegate
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à padre in modo speciale. Questo tipo di attività non sincronizzata viene chiamata attività nidificata scollegata o attività figlia scollegata. L'esempio seguente mostra un'attività che crea un'attività figlia distaccata.
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.");
// The example displays the following output:
// Outer task beginning.
// Outer task completed.
// Detached task completed.
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.")
' The example displays the following output:
' Outer task beginning.
' Outer task completed.
' Detached child completed.
Nota
L'attività padre non attende il completamento dell'attività figlio scollegata.
Creazione di attività figlie
Quando il codice utente in esecuzione in un'attività crea un'attività con l'opzione AttachedToParent, la nuova attività è nota come attività figlio associato dell'attività principale. È possibile usare l'opzione AttachedToParent per esprimere il parallelismo delle attività strutturate perché l'attività padre attende implicitamente la conclusione di tutte le attività figlie associate. L'esempio seguente mostra un'attività padre che crea 10 attività figlio associate. Nell'esempio viene chiamato il metodo Task.Wait per aspettare che l'attività padre finisca. Non è necessario attendere esplicitamente il completamento delle attività figlie associate.
using System;
using System.Threading;
using System.Threading.Tasks;
public class Child
{
public static void Main()
{
var parent = Task.Factory.StartNew(() => {
Console.WriteLine("Parent task beginning.");
for (int ctr = 0; ctr < 10; ctr++) {
int taskNo = ctr;
Task.Factory.StartNew((x) => {
Thread.SpinWait(5000000);
Console.WriteLine("Attached child #{0} completed.",
x);
},
taskNo, TaskCreationOptions.AttachedToParent);
}
});
parent.Wait();
Console.WriteLine("Parent task completed.");
}
}
// The example displays output like the following:
// Parent task beginning.
// Attached child #9 completed.
// Attached child #0 completed.
// Attached child #8 completed.
// Attached child #1 completed.
// Attached child #7 completed.
// Attached child #2 completed.
// Attached child #6 completed.
// Attached child #3 completed.
// Attached child #5 completed.
// Attached child #4 completed.
// Parent task completed.
Imports System.Threading
Namespace Child
Module Example
Public Sub Main()
Dim parent = Task.Factory.StartNew(Sub()
Console.WriteLine("Parent task beginning.")
For ctr As Integer = 0 To 9
Dim taskNo As Integer = ctr
Task.Factory.StartNew(Sub(x)
Thread.SpinWait(5000000)
Console.WriteLine("Attached child #{0} completed.",
x)
End Sub,
taskNo, TaskCreationOptions.AttachedToParent)
Next
End Sub)
parent.Wait()
Console.WriteLine("Parent task completed.")
End Sub
End Module
' The example displays output like the following:
' Parent task beginning.
' Attached child #9 completed.
' Attached child #0 completed.
' Attached child #8 completed.
' Attached child #1 completed.
' Attached child #7 completed.
' Attached child #2 completed.
' Attached child #6 completed.
' Attached child #3 completed.
' Attached child #5 completed.
' Attached child #4 completed.
' Parent task completed.
End Namespace
Un'attività padre può usare l'opzione TaskCreationOptions.DenyChildAttach per impedire l'associazione di altre attività all'attività padre. Per altre informazioni, vedere attività figlio collegate e scollegate.
In attesa del completamento delle attività
I tipi System.Threading.Tasks.Task e System.Threading.Tasks.Task<TResult> forniscono diversi overload dei metodi Task.Wait che consentono di attendere il completamento di un'attività. Inoltre, gli overload dei metodi statici Task.WaitAll e Task.WaitAny consentono di attendere il completamento di una o più di una matrice di attività.
In genere, è necessario attendere un'attività per uno dei motivi seguenti:
Il thread principale dipende dal risultato finale calcolato da un'attività.
È necessario gestire le eccezioni che potrebbero essere generate dall'attività.
L'applicazione potrebbe terminare prima che tutte le attività abbiano completato l'esecuzione. Ad esempio, le applicazioni console termineranno dopo l'esecuzione di tutto il codice sincrono in
Main
(punto di ingresso dell'applicazione).
L'esempio seguente illustra il modello di base che non comporta la gestione delle eccezioni:
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...
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...
Per un esempio che mostra la gestione delle eccezioni, vedere gestione delle eccezioni.
Alcuni overload consentono di specificare un timeout e altri accettano un CancellationToken aggiuntivo come parametro di input in modo che l'attesa stessa possa essere annullata a livello di codice o in risposta all'input dell'utente.
Quando si attende un'attività, si attende in modo implicito tutti gli elementi figlio di tale attività creati usando l'opzione TaskCreationOptions.AttachedToParent. Task.Wait restituisce immediatamente se l'attività è già stata completata. Un metodo Task.Wait genererà eventuali eccezioni generate da un'attività, anche se il metodo Task.Wait è stato chiamato dopo il completamento dell'attività.
Composizione delle attività
Le classi Task e Task<TResult> forniscono diversi metodi che consentono di comporre più attività. Questi metodi implementano modelli comuni e usano meglio le funzionalità del linguaggio asincrono fornite da C#, Visual Basic e F#. In questa sezione vengono descritti i metodi WhenAll, WhenAny, Delaye FromResult .
Task.WhenAll
Il metodo Task.WhenAll attende in modo asincrono il completamento di oggetti Task o Task<TResult> multipli. Fornisce versioni sovraccarico che consentono di attendere insiemi di attività non uniformi. Ad esempio, è possibile attendere il completamento di oggetti Task e Task<TResult> da una sola chiamata al metodo.
Task.WhenAny
Il metodo Task.WhenAny attende in modo asincrono il completamento di uno di più oggetti Task o Task<TResult>. Come nel metodo Task.WhenAll, questo metodo fornisce versioni sovraccaricate che consentono di attendere i set di attività non uniformi. Il metodo WhenAny è particolarmente utile negli scenari seguenti:
operazioni ridondanti: si consideri un algoritmo o un'operazione che può essere eseguita in molti modi. È possibile usare il metodo WhenAny per selezionare prima l'operazione che termina e quindi annullare le operazioni rimanenti.
Operazioni intercalate: È possibile avviare più operazioni che devono terminare e utilizzare il metodo WhenAny per elaborare i risultati al termine di ciascuna operazione. Al termine di un'operazione, è possibile avviare una o più attività.
operazioni rallentate: è possibile usare il metodo WhenAny per estendere lo scenario precedente limitando il numero di operazioni simultanee.
operazioni scadute: è possibile usare il metodo WhenAny per selezionare tra una o più attività e un'attività che termina dopo un'ora specifica, ad esempio un'attività restituita dal metodo Delay. Il metodo Delay è descritto nella sezione seguente.
Task.Delay
Il metodo Task.Delay produce un oggetto Task che termina dopo l'ora specificata. È possibile utilizzare questo metodo per creare cicli che eseguano il polling dei dati, specificare timeout, ritardare la gestione dell'input dell'utente e così via.
Task(T).FromResult
Usando il metodo Task.FromResult, è possibile creare un oggetto Task<TResult> che contiene un risultato precalcorato. Questo metodo è utile quando si esegue un'operazione asincrona che restituisce un oggetto Task<TResult> e il risultato di tale oggetto Task<TResult> è già calcolato. Per un esempio che usa FromResult per recuperare i risultati delle operazioni di download asincrone contenute in una cache, vedere procedura : Creare attività pre-calcolate.
Gestione delle eccezioni nelle attività
Quando un'attività genera una o più eccezioni, le eccezioni vengono incluse in un'eccezione AggregateException. Tale eccezione viene propagata nuovamente al thread che si unisce all'attività. In genere, è il thread in attesa del completamento dell'attività o del thread che accede alla proprietà Result. Questo comportamento applica i criteri di .NET Framework che tutte le eccezioni non gestite per impostazione predefinita devono terminare il processo. Il codice chiamante può gestire le eccezioni usando uno degli elementi seguenti in un blocco try
/catch
:
Il thread di unione può anche gestire le eccezioni accedendo alla proprietà Exception prima che l'attività venga sottoposta a raccolta dei rifiuti. Accedendo a questa proprietà, si impedisce all'eccezione non gestita di attivare il comportamento di propagazione delle eccezioni che termina il processo quando l'oggetto viene finalizzato.
Per altre informazioni sulle eccezioni e sulle attività, vedere gestione delle eccezioni.
Annullamento delle attività
La classe Task supporta l'annullamento cooperativo ed è completamente integrata con le classi System.Threading.CancellationTokenSource e System.Threading.CancellationToken introdotte in .NET Framework 4. Molti dei costruttori nella classe System.Threading.Tasks.Task accettano un oggetto CancellationToken come parametro di input. Molti degli overload StartNew e Run includono anche un parametro CancellationToken.
È possibile creare il token ed eseguire la richiesta di annullamento in un secondo momento usando la classe CancellationTokenSource. Trasmetti il token al Task come argomento e fai riferimento allo stesso token nel delegato dell'utente, che si occupa di rispondere a una richiesta di annullamento.
Per ulteriori informazioni, vedere Annullamento delle attività e Procedura: Annullare un'attività e i relativi elementi figlio .
Classe TaskFactory
La classe TaskFactory fornisce metodi statici che incapsulano modelli comuni per la creazione e l'avvio di attività e attività di continuazione.
Il modello più comune è StartNew, che crea e avvia un'attività in un'unica dichiarazione.
Quando si creano attività di continuazione da più elementi precedenti, usare il metodo ContinueWhenAll o il metodo ContinueWhenAny o i relativi equivalenti nella classe Task<TResult>. Per altre informazioni, vedere Concatenamento delle attività tramite task di continuazione.
Per incapsulare i metodi
BeginX
eEndX
del modello di programmazione asincrona in un'istanza di Task o di Task<TResult>, utilizzare i metodi FromAsync. Per altre informazioni, vedere TPL e la programmazione asincrona tradizionale di .NET Framework.
È possibile accedere al TaskFactory predefinito come proprietà statica nella classe Task o Task<TResult>. È anche possibile creare un'istanza di un TaskFactory direttamente e specificare varie opzioni che includono un'opzione CancellationToken, un'opzione TaskCreationOptions, un'opzione TaskContinuationOptions o un'opzione TaskScheduler. Tutte le opzioni specificate quando si crea la fabbrica di attività verranno applicate a tutte le attività create a meno che il Task non venga creato usando l'enumerazione TaskCreationOptions, nel qual caso le opzioni dell'attività prioritizzano quelle della fabbrica di attività.
Attività senza delegati
In alcuni casi, è possibile usare un Task per incapsulare un'operazione asincrona eseguita da un componente esterno anziché dal delegato dell'utente. Se l'operazione è basata sul modello di inizio/fine del modello di programmazione asincrona, è possibile usare i metodi FromAsync. In caso contrario, è possibile usare l'oggetto TaskCompletionSource<TResult> per incapsulare l'operazione in un'attività e ottenere così alcuni vantaggi della programmabilità Task. Ad esempio, il supporto per la propagazione e le continuazioni delle eccezioni. Per altre informazioni, vedere TaskCompletionSource<TResult>.
Pianificatori personalizzati
La maggior parte degli sviluppatori di applicazioni o librerie non è occupata dal processore in cui viene eseguita l'attività, dal modo in cui sincronizza il lavoro con altre attività o dal modo in cui è pianificata nel System.Threading.ThreadPool. Richiedono solo l'esecuzione il più efficiente possibile nel computer host. Se è necessario un controllo più dettagliato sui dettagli di pianificazione, il TPL consente di configurare alcune impostazioni nell'utilità di pianificazione predefinita e persino di fornire un'utilità di pianificazione personalizzata. Per altre informazioni, vedere TaskScheduler.
Strutture di dati correlate
Il TPL include diversi nuovi tipi pubblici utili in scenari paralleli e sequenziali. Sono incluse diverse classi di raccolta thread-safe, veloci e scalabili nello spazio dei nomi System.Collections.Concurrent e diversi nuovi tipi di sincronizzazione. Ad esempio, System.Threading.Semaphore e System.Threading.ManualResetEventSlim, che sono più efficienti dei predecessori per tipi specifici di carichi di lavoro. Altri nuovi tipi in .NET Framework 4, ad esempio, System.Threading.Barrier e System.Threading.SpinLock, forniscono funzionalità non disponibili nelle versioni precedenti. Per altre informazioni, vedere Strutture di dati per la programmazione parallela.
Tipi di attività personalizzati
È consigliabile non ereditare da System.Threading.Tasks.Task o System.Threading.Tasks.Task<TResult>. È invece consigliabile utilizzare la proprietà AsyncState per associare dati o stati aggiuntivi a un oggetto Task o Task<TResult>. È anche possibile usare metodi di estensione per estendere la funzionalità delle classi Task e Task<TResult>. Per altre informazioni sui metodi di estensione, vedere metodi di estensione e metodi di estensione .
Se è necessario ereditare da Task o Task<TResult>, non è possibile usare le classi Run o System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult>o System.Threading.Tasks.TaskCompletionSource<TResult> per creare istanze del tipo di attività personalizzato. Non è possibile usarli perché queste classi creano solo Task e Task<TResult> oggetti. Inoltre, non è possibile usare i meccanismi di continuazione delle attività forniti da Task, Task<TResult>, TaskFactorye TaskFactory<TResult> per creare istanze del tipo di attività personalizzato. Non è possibile usarle perché anche queste classi creano solo Task e oggetti Task<TResult>.
Sezioni correlate
Titolo | Descrizione |
---|---|
concatenamento delle attività tramite attività di continuazione | Descrive il funzionamento delle continuazioni. |
attività figlio collegate e scollegate | Descrive la differenza tra le attività figlie collegate e scollegate. |
Annullamento attività | Descrive il supporto per l'annullamento integrato nell'oggetto Task. |
gestione delle eccezioni | Viene descritto il modo in cui vengono gestite le eccezioni nei thread simultanei. |
Procedura: Usare Parallel.Invoke per eseguire operazioni parallele | Viene descritto come usare Invoke. |
Procedura: Restituire un valore da un'attività | Viene descritto come restituire valori dalle attività. |
Procedura: Annullare un'attività e i relativi elementi figlio | Descrive come annullare le attività. |
Procedura: Creare attività pre-computate | Viene descritto come utilizzare il metodo Task.FromResult per recuperare i risultati delle operazioni di download asincrone contenute in una cache. |
Procedura: Attraversare un albero binario con attività parallele | Viene descritto come usare le attività per attraversare un albero binario. |
Procedura: Svolgere un'attività annidata | Illustra come usare il metodo di estensione Unwrap. |
parallelismo dei dati | Viene descritto come usare For e ForEach per creare cicli paralleli sui dati. |
programmazione parallela | Nodo di primo livello per la programmazione parallela di .NET Framework. |