¿Qué es la programación concurrente (programación TPL) en .NET?

Escrito al frente
Una característica clave de un buen software es la concurrencia. En las últimas décadas, podemos hacer programación concurrente, pero es muy difícil. En el pasado, era difícil escribir, depurar y mantener software concurrente, lo que hacía que muchos desarrolladores abandonaran la programación concurrente para evitar problemas. Las funciones de biblioteca y lenguaje de la nueva versión de .NET han facilitado mucho la programación simultánea. Con el lanzamiento de Visual Studio 2012, Microsoft ha reducido significativamente el umbral para la programación concurrente. En el pasado, solo los expertos podían realizar programación concurrente, pero hoy en día todos los desarrolladores pueden (y deben) aceptar la programación concurrente.

Responda la pregunta: la diferencia entre .NET Core sincrónico y asincrónico

public ActionResult PushFileData ([FromBody] Web_PushFileData file) // Sincrónico
público async ActionResult PushFileData ([FromBody] Web_PushFileData file) // Asynchronous
Question: Para métodos sincrónicos, ¿cada solicitud usa el mismo hilo? Por ejemplo, si el cliente A solicita una acción sincrónica, la solicitud del cliente B se bloqueará antes de que se complete la ejecución.
Para los métodos asincrónicos, ¿cada solicitud toma un hilo inactivo del grupo de hilos para ejecutar el método? Es decir, los métodos de solicitud del cliente A y del cliente B se ejecutan por separado en diferentes subprocesos.

Volver al principio Conceptos básicos de
navegación

Conceptos básicos del hilo
TPL de programación concurrente

¿Por qué Windows debería admitir subprocesos?
Sobrecarga de subprocesos
Desarrollo de CPU
Razones para usar subprocesos
Cómo escribir un bucle Parallel.For simple

Datos
paralelos Paralelo Para perfilar
datos de defectos Tarea paralela y potencial

No asuma que el paralelismo siempre es rápido.
Evite escribir en cachés compartidos.
Evite llamar a métodos no seguros para subprocesos.
Muchas computadoras personales y estaciones de trabajo tienen CPU de varios núcleos que pueden ejecutar varios subprocesos simultáneamente. Para aprovechar al máximo el hardware, puede paralelizar el código para distribuir el trabajo entre varios procesadores.

   在过去,并行需要对线程和锁进行低级操作。Visual Studio和.NET框架通过提供运行时、类库类型和诊断工具来增强对并行编程的支持。这些特性是在.NET Framework 4中引入的,它们使得并行编程变得简单。您可以用自然的习惯用法编写高效、细粒度和可伸缩的并行代码,而无需直接处理线程或线程池。

La siguiente figura muestra la arquitectura de programación paralela en el marco .NET.
Inserte la descripción de la imagen aquí

Volver arriba
1 Conceptos básicos
1.1 Programación
concurrente Concurrencia

Haz varias cosas al mismo tiempo

   这个解释直接表明了并发的作用。终端用户程序利用并发功能,在输入数据库的同时响应用户输入。服务器应用利用并发,在处理第一个请求的同时响应第二个请求。只要你希望程序同时做多件事情,你就需要并发。

Multihilo

   并发的一种形式,它采用多个线程来执行程序。从字面上看,多线程就是使用多个线程。多线程是并发的一种形式,但不是唯一的形式。

Procesamiento en paralelo

Divida una gran cantidad de tareas que se están ejecutando en partes pequeñas y asígnelas a varios subprocesos que se ejecutan al mismo tiempo.

   为了让处理器的利用效率最大化,并行处理(或并行编程)采用多线程。当现代多核 CPU行大量任务时,若只用一个核执行所有任务,而其他核保持空闲,这显然是不合理的。

   并行处理把任务分割成小块并分配给多个线程,让它们在不同的核上独立运行。并行处理是多线程的一种,而多线程是并发的一种。

Programación asincrónica

Una forma de simultaneidad que utiliza un modo futuro o un mecanismo de devolución de llamada para evitar hilos innecesarios.

   一个 future(或 promise)类型代表一些即将完成的操作。在 .NET 中,新版 future 类型

Hay Tarea y Tarea. En la antigua API de programación asincrónica, se usaban devoluciones de llamada o eventos en lugar de
futuros. El concepto central de la programación asincrónica es la operación asincrónica: la operación iniciada se completará después de un período de tiempo. Mientras
se ejecuta esta operación , el hilo original no se bloqueará. El hilo que inició esta operación puede continuar realizando otras tareas. Cuando
se complete la operación, notificará su futuro o llamará a una función de devolución de llamada para que el programa sepa que la operación ha finalizado.

   NOTE:通常情况下,一个并发程序要使用多种技术。大多数程序至少使用了多线程(通过线程池)和异步编程。要大胆地把各种并发编程形式进行混合和匹配,在程序的各个部分使用

La herramienta adecuada.

1.2 TPL
Task Parallel Library (TPL) es un conjunto de tipos públicos y API en los espacios de nombres System.Threading y System.Threading.Tasks.

   TPL动态地扩展并发度,以最有效地使用所有可用的处理器。通过使用TPL,您可以最大限度地提高代码的性能,同时专注于您的代码的业务实现。

A partir de .NET Framework 4, TPL es la forma preferida de escribir código paralelo y multiproceso.

Volver arriba
2 Conceptos básicos de subprocesos
2.1 ¿Por qué Windows admite subprocesos?
En los primeros días de las computadoras, los sistemas operativos no proporcionaban el concepto de subprocesos. De hecho, todo el sistema ejecuta solo un subproceso de ejecución (un solo subproceso), que contiene tanto el código del sistema operativo como el código de la aplicación. El problema de usar solo un subproceso de ejecución es que las tareas de ejecución prolongada evitarán que se ejecuten otras tareas.
Por ejemplo, en aquellos días de Windows de 16 bits, la aplicación que imprimía un documento podía "congelar" fácilmente toda la máquina, haciendo que el sistema operativo y otras aplicaciones dejaran de responder. Algunos programas contienen errores que pueden provocar bucles sin fin. Encontró este problema, el usuario tuvo que reiniciar la computadora. Los usuarios lo odian.

   于是微软下定决心设计一个新的OS,这个OS必须健壮,可靠,易于是伸缩以安全,同同时必须改进16位windows的许多不足。

   微软设计这个OS内核时,他们决定在一个进程(Process)中运行应用程序的每个实例。进程不过是应用程序的一个实例要使用的资源的一个集合。每个进程都被赋予一个虚拟地址空间,确保一个进程使用的代码和数据无法由另一个进程访问。这就确保了应用程序实例的健壮性。由于应用程序破坏不了其他应用程序或者OS本身,所以用户的计算体验变得更好了。

   听起来似乎不错,但CPU本身呢?如果一个应用程序进入无限循环,会发生什么呢?如果机器中只有一个CPU,它会执行无限循环,不能执行其它任何东西。所以,虽然数据无法被破坏,而且更安全,但系统仍然可能停止响应。微软要修复这个问题,他们拿出的方案就是线程。作为Windows概念,线程的职责是对CPU进行虚拟化。Windows为每个进程都提供了该进程专用的专用的线程(功能相当于一个CPU,可将线程理解成一个逻辑CPU)。如果应用程序的代码进入无限循环,与那个代码关联的进程会被“冻结”,但其他进程(他们有自己的线程)不会冻结:他们会继续执行!

2.2 Sobrecarga de subprocesos Los subprocesos
son un concepto muy poderoso porque permiten que las ventanas respondan en cualquier momento, incluso cuando se realizan tareas de larga duración. Además, los subprocesos permiten a los usuarios utilizar una aplicación (como el "Administrador de tareas") para cerrar forzosamente una aplicación que parece estar congelada (también puede estar realizando una tarea de larga duración). Sin embargo, como todos los mecanismos de virtualización, los subprocesos incurrirán en gastos de espacio (consumo de memoria) y tiempo (rendimiento de ejecución en tiempo de ejecución).

   创建线程,让它进驻系统以及最后销毁它都需要空间和时间。另外,还需要讨论一下上下文切换。单CPU的计算机一次只能做一件事情。所以,windows必须在系统中的所有线程(逻辑CPU)之间共享物理CPU。

   在任何给定的时刻,Windows只将一个线程分配给一个CPU。那个线程允许运行一个时间片。一旦时间片到期,Windows就上下文切换到另一个给线程。每次上下文切换都要求Windows执行以下操作:

Guarde el valor en el registro de la CPU en una estructura de contexto dentro del objeto del núcleo del hilo que se está ejecutando actualmente.
Seleccione un hilo del conjunto de hilos existente para la programación (el hilo del tutorial básico de Python de destino al que cambiar ). Si el hilo es propiedad de otro proceso, Window debe cambiar el espacio de direcciones virtuales "visible" a la CPU antes de que comience a ejecutar cualquier código o tocar cualquier dato.
Cargue el valor en la estructura de contexto seleccionada en el registro de la CPU.
Una vez que se completa el cambio de contexto, la CPU ejecuta el subproceso seleccionado hasta que expira su intervalo de tiempo. Entonces, ocurrirá una nueva ronda de cambio de contexto. Windows realiza un cambio de contexto aproximadamente cada 30 ms.

   上下文切换是净开销:也就是说上下文切换所产生的开销不会换来任何内存或性能上的收益。

   根据上述讨论,我们的结论是必须尽可能地避免使用线程,因为他们要耗用大量的内存,而且需要相当多的时间来创建,销毁和管理。Windows在线程之间进行上下文切换,以及在发生垃圾回收的时候,也会浪费不少时间。然而,根据上述讨论,我们还得出一个结论,那就是有时候必须使用线程,因为它们使Windows变得更健壮,反应更灵敏。

   应该指出的是,安装了多个CPU或者一个多核CPU)的计算机可以真正同时运行几个线程,这提升了应用程序的可伸缩性(在少量的时间里做更多工作的能力)。Windows为每个CPU内核都分配一个线程,每个内核都自己执行到其他线程的上下文切换。Windows确保单个线程不会在多个内核上同时被调度,因为这会代理巨大的混乱。今天,许多计算机都包含了多个CPu,超线程CPU或者多核CPU。但是,windows最初设计时,单CPU计算机才是主流,所以Windows设计了线程来增强系统的响应能力和可靠性。今天,线程还被用于增强应用程序的可伸缩性,但在只有多CPU(或多核CPU)计算机上才有可能发生。

SUGERENCIA: Al final de un intervalo de tiempo, si Windows decide programar el mismo hilo de nuevo (en lugar de cambiar a otro hilo), Windows no realizará un cambio de contexto. El hilo seguirá ejecutándose, lo que mejora significativamente el rendimiento. Preste atención al diseñar su propio código y trate de evitar lo que se puede evitar en el tutorial de cambio de contexto de c # .

2.3 El desarrollo de la CPU En el
pasado, la velocidad de la CPU se ha acelerado con el tiempo. Por lo tanto, un programa que se ejecuta lentamente en una máquina vieja generalmente será más rápido en una máquina nueva. Sin embargo, el fabricante de la CPU no continuó con el tutorial de CPU vb.net

La tendencia se acelera. Debido a que los fabricantes de CPU no siempre pueden aumentar la velocidad de las CPU, se enfocan en hacer que los transistores sean cada vez más pequeños, de modo que se puedan acomodar más transistores en un chip. Hoy en día, un chip de silicio puede contener dos o más núcleos de CPU. De esta manera, si se pueden usar varios núcleos al escribir software, el software puede ejecutarse más rápido.

Las computadoras actuales utilizan las siguientes tres tecnologías de CPU múltiple.


Chips Hyper-Threading de CPU múltiple Chips
multinúcleo
2.4 Razones para
usar subprocesos Hay tres razones para usar subprocesos.

El uso de subprocesos puede aislar el código de otros códigos,
lo que mejorará la confiabilidad de la aplicación. De hecho, esta es la razón por la que Windows introdujo el concepto de subprocesos en el sistema operativo. La razón por la que Windows necesita subprocesos para su confiabilidad es porque su aplicación es un componente de terceros del sistema operativo, y Microsoft no verificará estos códigos antes de lanzar la aplicación. Si su aplicación admite la carga de componentes generados por otros fabricantes, la aplicación tendrá altos requisitos de robustez y el uso de hilos ayudará a satisfacer esta demanda.
Los subprocesos se pueden utilizar para simplificar la codificación.
A veces, si la tarea se ejecuta a través del propio subproceso de una tarea, o se utiliza un solo subproceso para manejar la tarea, la codificación se vuelve más simple. Sin embargo, si hace esto, debe utilizar recursos adicionales, y no es muy "económico" (no utilice la menor cantidad de código posible para lograr el objetivo). Ahora, incluso si tengo que pagar algunos recursos como precio, prefiero elegir un proceso de codificación simple. De lo contrario, insista en escribir siempre programas en lenguaje de máquina, no hay necesidad de convertirse en desarrollador de C #. Pero a veces, al usar hilos, algunas personas sienten que han elegido un método de codificación más fácil, pero de hecho, complican enormemente las cosas (y su código). Por lo general, cuando introduce subprocesos, introduce códigos que quieren cooperar entre sí. Pueden requerir construcciones de sincronización de subprocesos para saber cuándo termina otro subproceso. Una vez involucrada la colaboración, se deben utilizar más recursos y el código se volverá más complejo. Entonces, antes de comenzar a usar hilos, asegúrese de que los hilos realmente puedan ayudarlo.
Puede utilizar subprocesos para lograr una ejecución simultánea.
Si (y solo) sabe que su aplicación se ejecutará en una máquina con varias CPU, entonces dejar que se ejecuten varias tareas al mismo tiempo puede mejorar el rendimiento. Ahora las máquinas con múltiples CPU (o una CPU de múltiples núcleos) son bastante comunes, por lo que tiene sentido diseñar aplicaciones para usar múltiples núcleos.
Volver al principio
3 Paralelismo de
datos 3.1 Paralelismo de datos
El paralelismo de datos se refiere a la situación en la que se realizan las mismas operaciones en los elementos de la colección o matriz de origen al mismo tiempo (es decir, en paralelo). En las operaciones paralelas de datos, la colección de origen se particiona para que varios subprocesos puedan operar en diferentes segmentos al mismo tiempo.

El paralelismo de datos se refiere a la biblioteca paralela de tareas simultáneas (TPL) para los elementos de la colección o matriz de origen para admitir el paralelismo de datos a través de la clase system.threading.tasks.parallel. Esta clase proporciona implementaciones paralelas basadas en métodos de para y para cada bucle.

Escribe lógica de bucle para bucles paralelos, para o en paralelo, para cada bucle, al igual que escribe bucles secuenciales. No es necesario crear subprocesos ni poner en cola elementos de trabajo. En el ciclo básico, no es necesario utilizar bloqueos. TPL se ha encargado del trabajo subyacente por usted.

El siguiente código muestra secuencial y paralelo:

// Sequential version            
foreach (var item in sourceCollection)
{
    
    
    Process(item);
}

// Parallel equivalent
Parallel.ForEach(sourceCollection, item => Process(item));

 

Cuando se ejecuta un bucle paralelo, TPL divide la fuente de datos para que el bucle se pueda ejecutar en varias partes al mismo tiempo. En segundo plano, el programador de tareas divide las tareas según los recursos del sistema y la carga de trabajo. Si la carga de trabajo se desequilibra, el planificador redistribuirá el trabajo entre varios subprocesos y procesadores.
El siguiente código muestra cómo depurar el código a través de Visual Studio:

public static void test()
        {
    
    
            int[] nums = Enumerable.Range(0, 1000000).ToArray();
            long total = 0;
            
            // Use type parameter to make subtotal a long, not an int
            Parallel.For<long>(0, nums.Length, () => 0, (j, loop, subtotal) =>
            {
    
    
                subtotal += nums[j];
                return subtotal;
            },
                (x) => Interlocked.Add(ref total, x)
            );

            Console.WriteLine("The total is {0:N0}", total);
            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }

Seleccione Depurar> Iniciar depuración o presione F5.
La aplicación se inicia en modo de depuración y se detendrá en el punto de interrupción.
Abra el hilo en modo de interrupción seleccionando Depurar ventana> Windows> Subproceso. Debe estar en una sesión de depuración para abrir o ver subprocesos y otras ventanas de depuración.
Inserte la descripción de la imagen aquí

3.2 Análisis de Parallel.For
Ver la capa inferior de Parallel.For,

public static ParallelLoopResult For<TLocal>(int fromInclusive, int toExclusive, Func<TLocal> localInit, Func<int, ParallelLoopState, TLocal, TLocal> body, Action<TLocal> localFinally);
 

Veo claramente que hay una función func, que parece muy familiar.

 [TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")]
    public delegate TResult Func<out TResult>();

Resultó ser un delegado definido con múltiples sobrecargas. Consulte el documento para obtener más detalles:https://docs.microsoft.com/en-us/dotnet/api/system.func-4?view=netframework-4.7.2

De hecho, antes de TPL, para lograr concurrencia o subprocesos múltiples, básicamente se usaban delegados.

SUGERENCIA: Con respecto a la delegación, puede consultar (https://docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/delegates). O "Comisión detallada" (https://www.cnblogs.com/laoyu/archive/2013/01/13/2859000.html)

Volver al principio
4 Defectos potenciales
en el paralelismo de datos y tareas En muchos casos, el sistema paralelo para y paralelo puede proporcionar mejoras significativas en el rendimiento en comparación con los bucles secuenciales ordinarios. Sin embargo, el trabajo de bucles paralelos introduce complejidad, lo que puede causar problemas que no son comunes o que no se encuentran en absoluto en el código secuencial. Este tema enumera algunas prácticas para ayudarlo a evitar estos problemas al escribir código paralelo.

4.1 No asuma que el paralelismo es siempre rápido
En algunos casos, los bucles paralelos pueden correr más lento que sus lazos secuenciales equivalentes. La regla básica es que es poco probable que se aceleren los ciclos paralelos con pocas iteraciones y comisiones de usuario rápidas. Sin embargo, dado que hay muchos factores que pueden afectar el rendimiento, le sugiero que mida los resultados reales.

4.2 Evite escribir en la caché compartida
En código secuencial, es normal leer y escribir variables o campos estáticos. Sin embargo, siempre que varios subprocesos acceden a estas variables al mismo tiempo, existe un gran potencial para las condiciones de carrera. Incluso si puede usar bloqueos para sincronizar el acceso a las variables, el costo de la sincronización puede afectar el rendimiento. Por lo tanto, le recomendamos que evite o al menos limite el acceso al estado compartido en bucles paralelos tanto como sea posible. La mejor manera es utilizar los métodos sobrecargados de Parallel.For y Parallel.ForEach. Durante el ciclo paralelo, utilizan la variable de tipo genérico System.Threading.ThreadLocal para almacenar el estado local del hilo. Al usar bucles paralelos, incurrirá en la sobrecarga de dividir la colección de origen y sincronizar los subprocesos de trabajo. Los beneficios de la paralelización están aún más limitados por la cantidad de procesadores en la computadora. La ejecución de varios subprocesos de enlace computacional en un solo procesador no acelera la velocidad. Por lo tanto, tenga cuidado de no abusar del paralelismo.

El escenario más común de uso excesivo del paralelismo ocurre en bucles anidados. En la mayoría de los casos, es mejor utilizar el paralelismo solo en el bucle exterior, a menos que se apliquen los siguientes escenarios:

El ciclo interno es muy largo
y está realizando costosos cálculos para cada pedido.
El sistema de destino tiene suficientes procesadores para manejar la cantidad de subprocesos generados por el procesamiento paralelo de consultas en pedidos de clientes.
En todos los casos, la mejor forma de determinar la mejor forma de consulta es probar y medir.

4.3 Evite llamar a métodos
no seguros para subprocesos La escritura de métodos de instancia no seguros para subprocesos desde bucles paralelos puede provocar daños en los datos, que pueden detectarse o no en el programa. Puede causar anomalías. En el siguiente ejemplo, varios subprocesos intentarán llamar al método FileStream.WriteByte al mismo tiempo, pero esto no es compatible.

FileStream fs = File.OpenWrite(path);
byte[] bytes = new Byte[10000000];
// ...
Parallel.For(0, bytes.Length, (i) => fs.WriteByte(bytes[i]));
 

Bienvenido a suscribirme a mi atención la plataforma pública de micro-canales smidge Ze algo que decir], para aprender más divertido esperando que
conozcas: Xiong Ze - amargo aprender música con
el número público: Bear Ze algo que decir
Fuente actual: https: // www .cnblogs.com / xiongze520 / p / 14271739.html
Fuente original: https://www.52interview.com/solutions/38

Supongo que te gusta

Origin blog.csdn.net/chinaherolts2008/article/details/112852878
Recomendado
Clasificación