¿El uso de la biblioteca de tareas (TPL) hace que una aplicación sea multiproceso?


Recientemente, cuando me entrevistaron, recibí esta pregunta.

P: ¿Ha escrito aplicaciones multiproceso?

A: Sí

P: ¿Te importa explicar más?

A: Usé Tasks (Biblioteca paralela de tareas) para llevar a cabo algunas tareas como waiting for some info from internet while loading UI. Esto mejora la usabilidad de mi aplicación.

P: Pero, ¿solo has usado TPL significa que has escrito una solicitud multithreaded?

Yo: (No estoy seguro de qué decir1)

Entonces, ¿cuál es exactamente una aplicación multihilo? Ser ¿es diferente de usar Tasks?

Author: svick, 2014-05-23

4 answers

Las tareas se pueden usarpara representar operaciones que tienen lugar en múltiples subprocesos, pero no tienen para. Un puede escribir aplicaciones TPL complejas que solo se ejecutan en un solo subproceso. Cuando tienes una tarea que, por ejemplo, representa una solicitud de red para algunos datos, esa tarea es no va a crear subprocesos adicionales para lograr ese objetivo. Tal programa es (esperemos) asíncrono, pero no necesariamente mutlithreaded.

El paralelismo es haciendo más de una cosa al mismo tiempo. Esto puede o no ser el resultado de múltiples subprocesos.

Vamos con una analogía aquí.


Así es como Bob cocina la cena:

  1. Llena una olla de agua, y la hierve.
  2. Luego pone pasta en el agua.
  3. Él drena la pasta cuando está hecha.
  4. Prepara los ingredientes para su salsa.
  5. Pone todos los ingredientes para su salsa en una cacerola.
  6. Él cocina su salsa.
  7. Pone su salsa en su pasta.
  8. Él come la cena.

Bob ha cocinado completamente sincrónicamente sin multihilo, asincronía o paralelismo al cocinar su cena.


Así es como Jane cocina la cena:

  1. Llena una olla de agua y comienza a hervirla.
  2. Ella prepara los ingredientes para su salsa.
  3. Ella pone la pasta en el agua hirviendo.
  4. Ella pone los ingredientes en el cacerola.
  5. Ella drena su pasta.
  6. Ella pone la salsa en su pasta.
  7. Ella come su cena.

Jane aprovechó la cocina asíncrona (sin ningún multihilo) para lograr paralelismo al cocinar su cena.


Así es como Servy cocina la cena:

  1. Le dice a Bob que hierva una olla de agua, ponga la pasta cuando esté lista y sirva la pasta.
  2. Le dice a Jane que prepare los ingredientes para la salsa, la cocine y luego servir sobre la pasta cuando esté hecho.
  3. Espera a que Bob y Jane terminen.
  4. Él come su cena.

Servy aprovechó múltiples hilos (trabajadores) que cada uno individualmente hizo su trabajo sincrónicamente, pero que trabajaron asincrónicamente entre sí para lograr el paralelismo.

Por supuesto, esto se vuelve aún más interesante si consideramos, por ejemplo, si nuestra estufa tiene dos quemadores o solo uno. Si nuestra estufa tiene dos quemadores, entonces nuestros dos hilos, Bob y Jane, ambos son capaces de hacer su trabajo sin meterse el uno en el otro, mucho. Pueden toparse un poco con los hombros, o cada uno tratar de agarrar algo del mismo gabinete de vez en cuando, por lo que cada uno se ralentizará un bit, pero no mucho. Si cada uno necesita compartir un solo quemador de la estufa, entonces en realidad no serán capaces de hacer mucho en absoluto cada vez que la otra persona está haciendo el trabajo. En ese caso, el trabajo realmente no se hará más rápido que tener una sola persona cocinando de forma totalmente sincrónica, como hace Bob cuando está solo. En este caso estamos cocinando con múltiples hilos, pero nuestra cocción no está paralelizada. No todo el trabajo multihilo es en realidad trabajo paralelo. Esto es lo que sucede cuando se ejecutan varios subprocesos en una máquina con una CPU. En realidad, no se realiza el trabajo más rápido que solo usar un hilo, porque cada hilo solo se turna para hacer el trabajo. (Eso no significa programas multiproceso son inútiles en CPU de un núcleo, no lo son, es solo que la razón para usarlos no es para mejorar la velocidad.)


Incluso podemos considerar cómo estos cocineros harían su trabajo utilizando la Biblioteca de Tareas Paralelas, para ver qué usos del TPL corresponden a cada uno de estos tipos de cocineros:

Así que primero tenemos a bob, simplemente escribiendo código normal no-TPL y haciendo todo sincrónicamente:

public class Bob : ICook
{
    public IMeal Cook()
    {
        Pasta pasta = PastaCookingOperations.MakePasta();
        Sauce sauce = PastaCookingOperations.MakeSauce();
        return PastaCookingOperations.Combine(pasta, sauce);
    }
}

Luego tenemos a Jane, que inicia dos operaciones asíncronas diferentes, luego espera ambos después de comenzar cada uno de ellos para calcular su resultado.

public class Jane : ICook
{
    public IMeal Cook()
    {
        Task<Pasta> pastaTask = PastaCookingOperations.MakePastaAsync();
        Task<Sauce> sauceTask = PastaCookingOperations.MakeSauceAsync();
        return PastaCookingOperations.Combine(pastaTask.Result, sauceTask.Result);
    }
}

Como recordatorio aquí, Jane está usando el TPL, y está haciendo gran parte de su trabajo en paralelo, pero solo está usando un solo hilo para hacer su trabajo.

Luego tenemos a Servy, que usa Task.Run para crear una tarea que representa hacer trabajo en otro hilo. Inicia a dos trabajadores diferentes, hace que cada uno haga un trabajo sincrónicamente, y luego espera a que ambos trabajadores terminen.

public class Servy : ICook
{
    public IMeal Cook()
    {
        var bobsWork = Task.Run(() => PastaCookingOperations.MakePasta());
        var janesWork = Task.Run(() => PastaCookingOperations.MakeSauce());
        return PastaCookingOperations.Combine(bobsWork.Result, janesWork.Result);
    }
}
 92
Author: Servy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-23 16:24:52

A Task es una promesa para que el trabajo futuro se complete. Al usarlo, puede usarlo para I/O basedtrabajo, que no requiere que esté utilizando múltiples subprocesos para la ejecución de código. Un buen ejemplo es usar la característica de C # 5 de async/await con HttpClient que hace el trabajo de E/S basado en red.

Sin embargo, puede aprovechar el TPL para hacer trabajo multiproceso. Por ejemplo, cuando se usa Task.Run o Task.Factory.Startnew para iniciar una tarea nueva, el trabajo entre bastidores se pone en cola para usted en el ThreadPool, que el TPL se abstrae para nosotros, lo que le permite utilizar múltiples hilos.

Un escenario común para usar múltiples subprocesos es cuando tiene un trabajo enlazado a la CPU que se puede hacer simultáneamente (en paralelo). Trabajar con una aplicación multiproceso conlleva una gran responsabilidad.

Así que vemos que trabajar con el TPL dosen't necasserly significa usar múltiples hilos, pero definitivamente puede aprovecharlo para hacer multihilo.

 4
Author: Yuval Itzchakov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-23 16:06:00

P: Pero, solo que hayas usado TPL significa que has escrito un aplicación multiproceso?

Pregunta inteligente, Tarea != Multi-threading, Usando TaskCompletionSource puede crear un Task que puede ejecutarse en un solo subproceso(puede ser solo un subproceso de interfaz de usuario).

Task es solo una abstracción sobre una operación que puede completarse en el futuro. No significa que el código sea multihilo. Normalmente Task implica multi-threading, no necesariamente siempre.

Y tenga en cuenta solo con el conocimiento de TPL no se puede decir que usted sabe multi-threading. Hay muchos conceptos que necesita cubrir.

  • Hilo
  • Primitivas de sincronización
  • Seguridad del hilo
  • Programación asíncrona
  • Programación paralela que forma parte de TPL.
  • y mucho más ...

Y, por supuesto, la biblioteca paralela de tareas también.

Nota: esta no es la lista completa, estos son solo de la parte superior de mi cabeza.


Recursos para aprender:

Para enhebrar solo, sugiero http://www.albahari.com/threading /

Para tutoriales en vídeo sugiero Pluralsight. Se paga, pero vale la pena el costo.

Por último, pero no menos importante: Sí, por supuesto, es Stackoverflow.

 3
Author: Sriram Sakthivel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:24

Mis 5 centavos: usted no tiene que involucrar hilos explícitamente con Task.Run o Task.Factory.StartNew para hacer su aplicación TPL multiproceso. Considere esto:

async static Task TestAsync()
{
    Func<Task> doAsync = async () =>
    {
        await Task.Delay(1).ConfigureAwait(false);
        Console.WriteLine(new { Thread.CurrentThread.ManagedThreadId });
    };

    var tasks = Enumerable.Range(0, 10).Select(i => doAsync());
    await Task.WhenAll(tasks);
}

// ...
TestAsync().Wait();

El código después de await dentro de doAsync se ejecuta simultáneamente en diferentes subprocesos. De manera similar, la concurrencia se puede introducir con async sockets API, HttpClient, Stream.ReadAsync o cualquier otra cosa que use un grupo de subprocesos (incluidos los subprocesos del grupo IOCP).

En sentido figurado, cada aplicación. NET es ya multiproceso, porque la Framework utiliza ThreadPool extensivamente. Incluso una simple aplicación de consola mostrará más de un subproceso para System.Diagnostics.Process.GetCurrentProcess().Threads.Count. Su entrevistador debería haberle preguntado si ha escrito código concurrente (o paralelo).

 1
Author: noseratio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-24 08:35:26