En los métodos Task.Factory.StartNew y Task.Run

Al crear tareas con los métodos Task.Factory.StartNew o Task.Run, debe tener en cuenta ciertos puntos importantes al escribir código asincrónico. En la mayoría de los casos, es aconsejable evitar el uso del método Task.Factory.StartNew si está trabajando con código asincrónico. Si está trabajando con código paralelo, diría que StartNew es una buena opción.

Un programador de tareas es un componente que se encarga de programar tareas; El marco .Net le proporciona dos programadores de tareas. Existe el programador de tareas predeterminado que se ejecuta en el grupo de subprocesos de .Net framework, y está el programador de tareas que se ejecuta en el contexto de sincronización de un objetivo específico. El programador de tareas predeterminado será suficiente la mayor parte del tiempo, pero también puede crear su propio programador de tareas personalizado para proporcionar funcionalidades adicionales. Para construir su propio programador de tareas personalizado, necesitaría crear una clase que amplíe la clase System.Threading.Tasks.TaskScheduler.

¿Cómo creo tareas utilizando la biblioteca de tareas paralelas?

Hay varias formas en las que puede crear e iniciar tareas en .Net. Debe hacer uso de la clase System.Threading.Tasks.Task o System.Threading.Tasks.Task para crear tareas (una unidad de trabajo programable). Mientras que el primero se usa para crear una tarea que no devuelve un valor, el segundo se usa para crear tareas que tienen valores de retorno. La propiedad Task.Factory es una instancia de la clase TaskFactory. Esta propiedad se utiliza para crear y programar tareas. Mientras que el método Task.Factory.StartNew funciona como una operación de bifurcación y se utiliza para crear e iniciar nuevas tareas, el método Wait funciona como una operación de unión y espera a que se complete la tarea.

El siguiente fragmento de código ilustra cómo puede utilizar el método Task.Factory.StartNew.

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

También puede crear una tarea mediante el método Task.Run como se muestra en el fragmento de código a continuación.

public async Task DoSomeWork()

        {

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

        }

        void TestMethod()

        {

            Console.WriteLine("Hello world!");

        }

Si desea devolver un valor de una tarea, puede aprovechar el método Task.FromResult como se muestra en el fragmento de código a continuación.

public async Task DoSomeWork()

   {

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

   }

private string GetMessage()

   {

      return "Hello world!";

   }

También puede crear tareas usando un delegado o una acción. El siguiente fragmento de código muestra cómo puede crear tareas mediante acciones y delegados.

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

task1.Start();

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

task2.Start();

También puede crear tareas usando lamba y métodos anónimos.

Task.Factory.StartNew y Task.Run

Task.Factory.StartNew es una forma rápida de crear e iniciar una tarea. Tenga en cuenta que una llamada a Task.Factory.StartNew es funcionalmente equivalente a crear una instancia de tarea y luego llamar al método Start en la instancia. Sin embargo, no se recomienda su uso por muchas razones. Si desea ejecutar código síncrono, Task.Factory.StartNew no es una buena opción.

Tenga en cuenta que si hay un programador de tareas disponible, el método StartNew ejecutará la tarea en ese programador de tareas. Por el contrario, si un programador no está disponible, ejecutará la tarea en un subproceso del grupo de subprocesos. Cabe señalar que Task.Factory.StartNew tiene como valor predeterminado TaskScheduler.Current y no TaskScheduler.Default.

Tenga en cuenta que una llamada a Task.Run (acción) es equivalente a la siguiente declaración: Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

Por el contrario, una llamada a Task.Factory.StartNew (acción) es equivalente a la siguiente declaración:

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

Si desea utilizar Task.Factory.StartNew si ha creado un programador de tareas personalizado y le pasa la instancia del programador explícitamente. Siempre recomendaría usar Task.Run ya que es mucho más simple y tiene valores predeterminados más seguros. En otras palabras, debemos evitar el uso de Task.Factory.StartNew a menos que sea necesario crear un programador de tareas y luego pasarlo explícitamente al llamar al método StartNew para crear una nueva tarea y programarla. Si tuviera que usar el método TaskFactory.StartNew de manera efectiva y confiable, debe usar un programador de tareas personalizado y luego especificar CancellationToken y TaskCreationOptions.

Se recomienda usar el método Task.Run cuando no es necesario tener un control detallado sobre la programación de subprocesos y sus complejidades. Debe usar Task.Run principalmente en métodos vinculados a la CPU. Sin embargo, debe usar Task.Run mientras invoca la tarea y no dentro de la implementación de la tarea. En otras palabras, debe usar Task.Run no dentro de ninguna implementación de un método, sino en el punto donde se llama al método. A modo de ejemplo, el siguiente fragmento de código es un ejemplo de código "incorrecto".

public async Task DownloadDataFromWebAsync(Uri uri)

        {

            return await Task.Run(() =>

            {

                using (WebClient webClient = new WebClient())

                {

                    return webClient.DownloadString(uri);

                }

            });

        }

Consulte el fragmento de código proporcionado anteriormente. El método no es escalable, ya que bloquearía el subproceso en segundo plano, recuperaría un subproceso del grupo de subprocesos y se ejecutaría de forma sincrónica en él. Por lo tanto, consumiría más recursos en su sistema.