O que é programação simultânea (programação TPL) em .NET?

Escrito antecipadamente
Uma característica chave de um bom software é a simultaneidade. Nas últimas décadas, podemos fazer programação simultânea, mas é muito difícil. No passado, era difícil escrever, depurar e manter software simultâneo, o que fazia com que muitos desenvolvedores desistissem da programação simultânea para evitar problemas. A biblioteca e os recursos de linguagem da nova versão do .NET tornaram a programação simultânea muito mais fácil. Com o lançamento do Visual Studio 2012, a Microsoft reduziu significativamente o limite para programação simultânea. No passado, apenas especialistas podiam fazer programação simultânea. Hoje, todo desenvolvedor pode (e deve) aceitar a programação simultânea.

Responda à pergunta: a diferença entre o .NET Core síncrono e assíncrono

public ActionResult PushFileData ([FromBody] Web_PushFileData file) // Synchronous
public async ActionResult PushFileData ([FromBody] Web_PushFileData file) // Asynchronous
Question: Para métodos síncronos, cada solicitação usa o mesmo thread? Por exemplo, se o cliente A solicitar uma ação síncrona, a solicitação do cliente B será bloqueada antes que a execução seja concluída.
Para métodos assíncronos, cada solicitação leva um thread inativo do pool de threads para executar o método? Ou seja, os métodos de solicitação do cliente A e do cliente B são executados separadamente em sub-threads diferentes.

Voltar ao topo Conceitos básicos de
navegação

Noções básicas de thread
TPL de programação simultânea

Por que o Windows deve oferecer suporte a threads? O desenvolvimento de CPU de
sobrecarga
de thread
. Motivos para usar threads?
Como escrever um loop Parallel.For simples

Dados
paralelos Paralelo. Para criar perfis de
dados de defeitos, tarefa paralela e potencial

Não presuma que o paralelismo é sempre rápido.
Evite gravar em caches compartilhados.
Evite chamar métodos não seguros para thread.
Muitos computadores pessoais e estações de trabalho têm CPUs de vários núcleos que podem executar vários threads simultaneamente. Para aproveitar ao máximo o hardware, você pode paralelizar o código para distribuir o trabalho por vários processadores.

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

A figura a seguir mostra a arquitetura de programação paralela na estrutura .NET.
Insira a descrição da imagem aqui

Voltar ao topo
1 Conceitos básicos
1.1 Concorrência de programação
simultânea

Faça várias coisas ao mesmo tempo

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

Multithreading

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

Processamento paralelo

Divida um grande número de tarefas em execução em pequenas partes e atribua-as a vários threads em execução ao mesmo tempo.

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

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

Programação assíncrona

Uma forma de simultaneidade que usa um modo futuro ou mecanismo de retorno de chamada para evitar threads desnecessários.

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

Existem tarefas e tarefas. Na antiga API de programação assíncrona, retornos de chamada ou eventos eram usados ​​em vez de
futuros. O conceito central da programação assíncrona é a operação assíncrona: a operação iniciada será concluída após um período de tempo. Enquanto esta operação
estiver sendo executada, o thread original não será bloqueado. O thread que iniciou esta operação pode continuar a executar outras tarefas. Quando a
operação for concluída, ele notificará seu futuro ou chamará uma função de retorno de chamada para informar ao programa que a operação foi encerrada.

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

A ferramenta certa.

1.2 TPL
Task Parallel Library (TPL) é um conjunto de tipos públicos e APIs nos namespaces System.Threading e System.Threading.Tasks.

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

A partir do .NET Framework 4, o TPL é a maneira preferida de escrever código multithread e paralelo.

Voltar ao topo
2 Noções básicas de threads
2.1 Por que o Windows oferece suporte a threads
No início dos computadores, os sistemas operacionais não forneciam o conceito de threads. Na verdade, todo o sistema executa apenas um thread de execução (thread único), que contém o código do sistema operacional e o código do aplicativo. O problema de usar apenas um thread de execução é que as tarefas de longa execução impedirão a execução de outras tarefas.
Por exemplo, naquela época do Windows de 16 bits, o aplicativo que imprimia um documento podia facilmente "congelar" a máquina inteira, fazendo com que o sistema operacional e outros aplicativos parassem de responder. Alguns programas contêm bugs, que podem causar loops intermináveis. Encontrado este problema, o usuário teve que reiniciar o computador. Os usuários odeiam.

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

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

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

2.2 Sobrecarga de thread Os
threads são um conceito muito poderoso porque permitem que as janelas respondam a qualquer momento, mesmo durante a execução de tarefas de longa duração. Além disso, os threads permitem que os usuários usem um aplicativo (como o "Gerenciador de Tarefas") para encerrar à força um aplicativo que parece estar congelado (ele também pode estar executando uma tarefa de longa duração). No entanto, como todos os mecanismos de virtualização, os threads incorrerão em sobrecarga de espaço (consumo de memória) e tempo (desempenho de execução em tempo de execução).

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

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

Salve o valor no registro da CPU em uma estrutura de contexto dentro do objeto kernel da thread em execução no momento.
Selecione um encadeamento do conjunto de encadeamentos existente para agendamento (o encadeamento do tutorial básico do Python de destino para o qual alternar ). Se a thread pertencer a outro processo, o Window deve mudar o espaço de endereço virtual "visível" para a CPU antes de começar a executar qualquer código ou tocar em qualquer dado.
Carregue o valor da estrutura de contexto selecionada no registro da CPU.
Depois que a troca de contexto é concluída, a CPU executa o thread selecionado até que seu intervalo de tempo expire. Então, uma nova rodada de troca de contexto ocorrerá. O Windows realiza uma mudança de contexto aproximadamente a cada 30 ms.

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

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

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

DICA: No final de um intervalo de tempo, se o Windows decidir agendar o mesmo encadeamento novamente (em vez de alternar para outro encadeamento), o Windows não executará uma troca de contexto. O thread continuará em execução, o que melhora significativamente o desempenho. Preste atenção ao projetar seu próprio código e tente evitar o que pode ser evitado no tutorial c # de mudança de contexto .

2.3 O desenvolvimento da CPU No
passado, a velocidade da CPU foi ficando mais rápida com o tempo. Portanto, um programa executado lentamente em uma máquina antiga geralmente será mais rápido em uma máquina nova. No entanto, o fabricante da CPU não deu continuidade ao tutorial da CPU vb.net

A tendência está chegando mais rápido. Como os fabricantes de CPUs nem sempre podem aumentar a velocidade das CPUs, eles se concentram em tornar os transistores cada vez menores, para que mais transistores possam ser acomodados em um chip. Hoje, um chip de silício pode conter 2 ou mais núcleos de CPU. Desta forma, se vários núcleos podem ser usados ​​ao escrever software, o software pode ser executado mais rápido.

Os computadores de hoje usam as três tecnologias multi-CPU a seguir.


Chips Hyper-Threading de CPU múltipla Chips de
vários núcleos
2.4 Razões para
usar threads Existem três razões para usar threads.

O uso de threads pode isolar o código de outros códigos, o
que aumentará a confiabilidade do aplicativo. Na verdade, essa é a razão pela qual o Windows introduziu o conceito de threads no sistema operacional. O motivo pelo qual o Windows precisa de threads para confiabilidade é porque seu aplicativo é um componente de terceiros do sistema operacional e a Microsoft não verificará esses códigos antes de você lançar o aplicativo. Se sua aplicação suportar o carregamento de componentes gerados por outros fabricantes, a aplicação terá altos requisitos de robustez e o uso de roscas ajudará a atender a essa demanda.
Threads podem ser usados ​​para simplificar a codificação.
Às vezes, se a tarefa for executada por um thread da própria tarefa, ou se um único thread for usado para lidar com a tarefa, a codificação se torna mais simples. No entanto, se você fizer isso, deverá usar recursos adicionais e não é muito "econômico" (não use o mínimo de código possível para atingir o objetivo). Agora, mesmo que eu tenha que pagar alguns recursos como preço, prefiro escolher um processo de codificação simples. Caso contrário, apenas insista em sempre escrever programas em linguagem de máquina, não há necessidade de se tornar um desenvolvedor C #. Mas às vezes, ao usar threads, algumas pessoas sentem que escolheram um método de codificação mais fácil, mas na verdade, eles complicam muito as coisas (e seu código). Normalmente, ao introduzir threads, você introduz códigos que desejam cooperar uns com os outros.Eles podem exigir construções de sincronização de threads para saber quando outro thread termina. Uma vez envolvida a colaboração, mais recursos devem ser usados ​​e o código se tornará mais complexo. Portanto, antes de começar a usar threads, certifique-se de que as threads podem realmente ajudá-lo.
Você pode usar threads para obter execução simultânea.
Se (e apenas) você souber que seu aplicativo será executado em uma máquina com várias CPUs, permitir que várias tarefas sejam executadas ao mesmo tempo pode melhorar o desempenho. Agora, máquinas com várias CPUs (ou uma CPU de vários núcleos) são bastante comuns, por isso faz sentido projetar aplicativos para usar vários núcleos.
Voltar ao início
3 Paralelismo de
dados 3.1 Paralelismo de dados
O paralelismo de dados refere-se à situação em que as mesmas operações são executadas nos elementos da coleção ou matriz de origem ao mesmo tempo (ou seja, em paralelo). Em operações paralelas de dados, a coleção de origem é particionada para que vários threads possam operar em diferentes segmentos ao mesmo tempo.

Paralelismo de dados refere-se à biblioteca paralela de tarefas simultâneas (TPL) para elementos na coleção ou matriz de origem para suportar o paralelismo de dados por meio da classe system.threading.tasks.parallel. Esta classe fornece implementações paralelas baseadas em métodos de e para cada loops.

Você escreve a lógica de loop para loop parallel.for ou parallel.foreach, da mesma forma que escreve loops sequenciais. Você não precisa criar threads ou itens de trabalho em fila. No loop básico, você não precisa usar bloqueios. A TPL cuidou do trabalho subjacente para você.

O código a seguir mostra sequencial e paralelo:

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

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

 

Quando um loop paralelo está em execução, o TPL particiona a fonte de dados para que o loop possa ser executado em várias partes ao mesmo tempo. Em segundo plano, o agendador de tarefas divide as tarefas com base nos recursos do sistema e na carga de trabalho. Se a carga de trabalho se tornar desequilibrada, o planejador redistribuirá o trabalho entre vários threads e processadores.
O código a seguir mostra como depurar o código por meio do 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();
        }

Selecione Depurar> Iniciar Depuração ou pressione F5.
O aplicativo inicia no modo de depuração e fará uma pausa no ponto de interrupção.
Abra o thread no modo de interrupção selecionando Window Debug> Windows> Thread. Você deve estar em uma sessão de depuração para abrir ou ver threads e outras janelas de depuração.
Insira a descrição da imagem aqui

3.2 Análise de Paralelo. Para
ver a camada inferior de Paralelo.

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

Vejo claramente que existe uma função func, que parece muito familiar.

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

Acabou por ser um delegado definido com múltiplas sobrecargas. Verifique o documento para obter detalhes:https://docs.microsoft.com/en-us/dotnet/api/system.func-4?view=netframework-4.7.2

Na verdade, antes do TPL, para obter simultaneidade ou multithreading, os delegados eram basicamente usados.

DICA: em relação à delegação, você pode verificar (https://docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/delegates). Ou "Comissão detalhada" (https://www.cnblogs.com/laoyu/archive/2013/01/13/2859000.html)

Voltar ao início
4 Defeitos potenciais
no paralelismo de dados e tarefas Em muitos casos, parallel.for e parallel.foreach podem fornecer melhorias de desempenho significativas em relação aos loops sequenciais comuns. No entanto, o trabalho de loops paralelos apresenta complexidade, que pode causar problemas que não são comuns ou não são encontrados em código sequencial. Este tópico enumera algumas práticas para ajudá-lo a evitar esses problemas ao escrever código paralelo.

4.1 Não presuma que o paralelismo é sempre rápido.Em
alguns casos, os loops paralelos podem ser executados mais lentamente do que os seus loops equivalentes sequenciais. A regra básica é que é improvável que os loops paralelos com poucas iterações e comissões rápidas do usuário sejam acelerados. No entanto, como há muitos fatores que podem afetar o desempenho, sugiro que você meça os resultados reais.

4.2 Evite gravar no cache compartilhado
No código sequencial, é normal ler e gravar variáveis ​​ou campos estáticos. No entanto, sempre que vários threads acessam essas variáveis ​​ao mesmo tempo, há um grande potencial para condições de corrida. Mesmo que você possa usar bloqueios para sincronizar o acesso às variáveis, o custo da sincronização pode prejudicar o desempenho. Portanto, recomendamos que você evite ou pelo menos limite o acesso ao estado compartilhado em loops paralelos tanto quanto possível. A melhor maneira é usar os métodos sobrecarregados de Parallel.For e Parallel.ForEach. Durante o loop paralelo, eles usam a variável de tipo genérico System.Threading.ThreadLocal para armazenar o estado local do segmento. Usando loops paralelos, você incorrerá na sobrecarga de dividir a coleção de origem e sincronizar os threads de trabalho. Os benefícios da paralelização são ainda mais limitados pelo número de processadores no computador. A execução de vários threads de ligação computacional em um único processador não acelera a velocidade. Portanto, tome cuidado para não abusar do paralelismo.

O cenário mais comum de uso excessivo de paralelismo ocorre em loops aninhados. Na maioria dos casos, é melhor usar paralelismo apenas no loop externo, a menos que os seguintes cenários sejam aplicáveis:

O loop interno é muito longo.
Você está realizando cálculos caros para cada pedido.
O sistema de destino tem processadores suficientes para lidar com o número de threads gerados pelo processamento paralelo de consultas em pedidos de clientes.
Em todos os casos, a melhor maneira de determinar a melhor forma de consulta é testar e medir.

4.3 Evite chamar métodos
não seguros para thread A gravação de métodos de instância não seguros para thread a partir de loops paralelos pode causar corrupção de dados, que pode ou não ser detectada no programa. Isso pode causar anormalidades. No exemplo a seguir, vários threads tentarão chamar o método FileStream.WriteByte ao mesmo tempo, mas isso não é suportado.

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

Bem-vindo ao subscrever a minha atenção a plataforma pública de micro-canal smidge Ze algo a dizer], para aprender mais divertido esperando por você para obter o conhecimento
de: Xiong Ze - amargo aprendizado de música com
o número público: Bear Ze algo a dizer
Fonte atual: https: // www .cnblogs.com / xiongze520 / p / 14271739.html
Fonte original: https://www.52interview.com/solutions/38

Acho que você gosta

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