Articoli di programmazione

Programmazione asincrona in C# con Task e Parallel

Giochiamo con queste due classi C# che permettono di eseguire codice asincrono facilmente nelle nostre applicazioni

photo-1510746001195-0db09655b6db.jpeg
Per prima cosa eseguiamo un semplice Test per provare l'esecuzione di un Task. Da notare ce facciamo addormentare il thread per un millisecondo, quanto basta per far eseguire prima la scrittura "Executed some other stuff" rispetto alla scrittura di "1 task executed":

[TestMethod]
public void SimpleTask()
{
Task task = Task.Run(() =>
{
Thread.Sleep(1);
Debug.WriteLine("1 Task executed");
return 10000;
});

// execute some other stuff
Debug.WriteLine("Executed some other stuff");

// then wait for end of the task's execution
task.Wait();
task.Result.Should().Be.EqualTo(10000);
}

// il test non fallisce ed il suo Output è:
// Executed some other stuff
// 1 Task executed

Ora vediamo l'esempio successivo, molto simile al precedente, ma con l'uso di ContinueWith. Da notare che è possibile reperire il risultato del primo Task all'interno di ContinueWith:

public void SimpleTask_ContinueWith()
{
var task = Task.Factory.StartNew(() =>
{
Thread.Sleep(10);
Debug.WriteLine("1 Task executed");
return 5000;
});

// using ContinueWith it is possible to handle the return of the first Task
task = task.ContinueWith((x) =>
{
Thread.Sleep(10);
Debug.WriteLine("2 Task executed");
return x.Result * 2;
});

// execute some other stuff
Debug.WriteLine("Executed some other stuff");

// then wait for end of the task's execution
task.Wait();
Debug.WriteLine("Return result");
task.Result.Should().Be.EqualTo(10000);
}

Ora vediamo come si cancellano i Task lanciati in esecuzione. Basta creare una istanza di CancellationTokenSource, ricavarne il Token e chiamare manualmente l'uscita dal metodo se si verifica IsCancellationRequested:

public void TaskCancellation()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;

var task = Task.Run(() =>
{
for (var i = 1; i < 10; i++)
{
if (token.IsCancellationRequested)
break;
Thread.Sleep(1000);
Debug.WriteLine("just one more time..." + i);
}
Debug.WriteLine("1 Task executed");
return 10000;
}, token);

// execute some other stuff
Thread.Sleep(3000);
Debug.WriteLine("Executed some other stuff");

tokenSource.Cancel();
Debug.WriteLine("Task Canceled!");
// then wait for end of the task's execution
task.Wait();
task.Result.Should().Be.EqualTo(10000);
}

Vediamo adesso la classe Parallel. C'è da notare che ovviamente eseguendo in parallelo l'azione su tutta la collection, l'ordine di esecuzione non è garantito:

public void SimpleParallelLoops()
{
var list = new List { 1, 2, 3, 4, 5 };

Debug.WriteLine("Standard ForEach:");
list.ForEach(a => Debug.WriteLine(a)); // the order is guaranteed

Debug.WriteLine("Parallel ForEach:");
Parallel.ForEach(list, (a) => Debug.WriteLine(a)); // the order is NOT guaranteed!
}

Vediamo infine l'uso di Invoke con Parallel:

public void SimpleInvoke()
{
// the order is NOT guaranteed!
Parallel.Invoke(
() => Debug.WriteLine(1),
() => Debug.WriteLine(2),
() => Debug.WriteLine(3),
() => Debug.WriteLine(4),
() => Debug.WriteLine(5),
() => Debug.WriteLine(6),
() => Debug.WriteLine(7),
() => Debug.WriteLine(8),
() => Debug.WriteLine(9),
() => Debug.WriteLine(10));
}


#csharp #async #tips