Nei metodi Task.Factory.StartNew e Task.Run

Quando si creano attività utilizzando i metodi Task.Factory.StartNew o Task.Run, è necessario tenere a mente alcuni punti importanti durante la scrittura di codice asincrono. Nella maggior parte dei casi, è consigliabile evitare di utilizzare il metodo Task.Factory.StartNew se si lavora con codice asincrono. Se stai lavorando con codice parallelo, direi che StartNew è una buona scelta.

Un'utilità di pianificazione è un componente responsabile della pianificazione delle attività; Il framework .Net ti fornisce due utilità di pianificazione. C'è l'utilità di pianificazione predefinita che viene eseguita sul pool di thread del framework .Net e c'è l'utilità di pianificazione che viene eseguita nel contesto di sincronizzazione di una destinazione specificata. L'utilità di pianificazione predefinita sarà sufficiente per la maggior parte del tempo, ma è anche possibile creare un'utilità di pianificazione delle attività personalizzata per fornire funzionalità aggiuntive. Per creare il proprio pianificatore di attività personalizzato è necessario creare una classe che estenda la classe System.Threading.Tasks.TaskScheduler.

Come si creano attività utilizzando la libreria Task Parallel?

Esistono diversi modi in cui è possibile creare e avviare attività in .Net. È necessario utilizzare la classe System.Threading.Tasks.Task o System.Threading.Tasks.Task per creare attività (un'unità di lavoro pianificabile). Mentre il primo viene utilizzato per creare un'attività che non restituisce un valore, il secondo viene utilizzato per creare attività che hanno valori di ritorno. La proprietà Task.Factory è un'istanza della classe TaskFactory. Questa proprietà viene utilizzata per creare e pianificare attività. Mentre il metodo Task.Factory.StartNew funziona come un'operazione di fork e viene utilizzato per creare e avviare nuove attività, il metodo Wait funziona proprio come un'operazione di join e attende il completamento dell'attività.

Il frammento di codice seguente illustra come utilizzare il metodo Task.Factory.StartNew.

Task.Factory.StartNew(() => TestMethod(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);

Puoi anche creare un'attività utilizzando il metodo Task.Run come mostrato nello snippet di codice di seguito.

public async Task DoSomeWork()

        {

            await Task.Run(() => TestMethod());

        }

        void TestMethod()

        {

            Console.WriteLine("Hello world!");

        }

Se desideri restituire un valore da un'attività, puoi sfruttare il metodo Task.FromResult come mostrato nel frammento di codice di seguito.

public async Task DoSomeWork()

   {

      string text = await Task.FromResult(GetMessage());

   }

private string GetMessage()

   {

      return "Hello world!";

   }

È inoltre possibile creare attività utilizzando un delegato o un'azione. Il frammento di codice seguente mostra come creare attività utilizzando azioni e delegati.

Task task1 = new Task (new Action(Display));

task1.Start();

Task task2 = new Task (delegate { Display(); });

task2.Start();

Puoi anche creare attività usando lamba e metodi anonimi.

Task.Factory.StartNew e Task.Run

Task.Factory.StartNew è un modo rapido per creare e avviare un'attività. Si noti che una chiamata a Task.Factory.StartNew è funzionalmente equivalente alla creazione di un'istanza di attività e quindi alla chiamata del metodo Start sull'istanza. Tuttavia, non è consigliabile utilizzarlo per molti motivi. Se desideri eseguire codice sincrono, Task.Factory.StartNew non è una buona scelta.

Si noti che se è disponibile un'utilità di pianificazione, il metodo StartNew eseguirà l'attività su tale pianificazione. Al contrario, se uno scheduler non è disponibile, eseguirà l'attività su un thread del pool di thread. Va notato che Task.Factory.StartNew ha come impostazione predefinita TaskScheduler.Current e non TaskScheduler.Default.

Si noti che una chiamata a Task.Run (azione) è equivalente alla seguente istruzione: Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

Al contrario, una chiamata a Task.Factory.StartNew (azione) è equivalente alla seguente istruzione:

Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current);

Se si desidera utilizzare Task.Factory.StartNew se è stato creato uno scheduler di attività personalizzato e si passa esplicitamente l'istanza dello scheduler. Consiglio sempre di utilizzare Task.Run in quanto è molto più semplice e ha impostazioni predefinite più sicure. In altre parole, dovremmo evitare di utilizzare Task.Factory.StartNew a meno che non sia necessario creare un'utilità di pianificazione e quindi passarla esplicitamente quando si chiama il metodo StartNew per creare una nuova attività e pianificarla. Se si utilizza il metodo TaskFactory.StartNew in modo efficace e affidabile, è necessario utilizzare un'utilità di pianificazione delle attività personalizzata e quindi specificare CancellationToken e TaskCreationOptions.

Si consiglia di utilizzare il metodo Task.Run quando non è necessario disporre di un controllo molto dettagliato sulla pianificazione dei thread e sulle sue complessità. È necessario utilizzare Task.Run principalmente sui metodi associati alla CPU. Tuttavia, è necessario utilizzare Task.Run mentre si richiama l'attività e non all'interno dell'implementazione dell'attività. In altre parole, dovresti usare Task.Run non all'interno di alcuna implementazione di un metodo, ma nel punto in cui viene chiamato il metodo. Ad esempio, il seguente frammento di codice è un esempio di un pezzo di codice "cattivo".

public async Task DownloadDataFromWebAsync(Uri uri)

        {

            return await Task.Run(() =>

            {

                using (WebClient webClient = new WebClient())

                {

                    return webClient.DownloadString(uri);

                }

            });

        }

Fare riferimento allo snippet di codice riportato sopra. Il metodo non è scalabile in quanto bloccherebbe il thread in background, recupererebbe un thread dal pool di thread ed eseguirà in modo sincrono su di esso. Quindi, consumerebbe più risorse nel tuo sistema.