đŸ’» OlĂĄ mundo da programação concorrente

maiquitome

Dev Maiqui đŸ‡§đŸ‡·

Posted on June 24, 2022

đŸ’» OlĂĄ mundo da programação concorrente

Nest post vamos dar uma introdução sobre a programação concorrente. Vamos abordar tambĂ©m um pouco sobre a diferença entre concorrĂȘncia e paralelismo, assuntos que podem parecer sinĂŽnimos, mas quando falamos em programação de computadores hĂĄ diferenças.

Neste post foram abordados os assuntos principais de cada pesquisa feita. Para mais detalhes sugiro a leitura ou visualização delas:

  1. Site da Intel;
  2. Livro C++ Concurrency in Action;
  3. Livro Erlang and OTP in Action;
  4. Vídeo O que são os NÚCLEOS, THREADS, DUO, QUAD?.
  5. Artigo Concurrency vs Parallelism
  6. Artigo Threads: O que sĂŁo e para que servem em um processador?

A Jornada do Autodidata em InglĂȘs

O que Ă© concorrĂȘncia?

Conforme o livro C++ Concurrency in Action a concorrĂȘncia Ă© sobre duas, ou mais atividades separadas/independentes que acontecem ao mesmo tempo. Encontramos a concorrĂȘncia como parte natural da vida; podemos caminhar e falar ao mesmo tempo, ou realizar açÔes diferentes com cada mĂŁo, e cada um pode viver a sua vida independentemente do outro - vocĂȘ pode ver futebol enquanto eu vou nadar, e assim por diante.

Image description


ConcorrĂȘncia em sistemas de computador

Quando falamos de concorrĂȘncia em computadores, nos referimos a um Ășnico sistema que executa mĂșltiplas atividades independentes ao mesmo tempo e sem ordem especĂ­fica, em vez de sequencialmente (ordem especĂ­fica).

Sequencialmente (ordem especĂ­fica):

Image description

Em um jogo de cartas, os jogadores jogam ao mesmo tempo, mas o jogo começa com o baralho sendo embaralhado, as cartas sendo entregues aos jogadores e, depois, cada jogador joga na sua vez, hå uma ordem especifica:
Image description

Ao mesmo tempo e sem ordem especĂ­fica:

Image description

Definição do livro Erlang and OTP in Action:

ConcorrĂȘncia Ă© apenas mais uma palavra para paralelo? Quase, mas nĂŁo exatamente, pelo menos quando estamos falando de computadores e programação.

Uma definição semiformal popular diz algo como: "Aquelas coisas que nĂŁo tĂȘm nada que as obrigue a acontecer em uma ordem especĂ­fica sĂŁo ditas como concorrentes". Por exemplo, dada a tarefa de ordenar dois baralhos de cartas, vocĂȘ poderia ordenar um baralho primeiro e depois o outro; ou se vocĂȘ tivesse braços e olhos extras, vocĂȘ poderia ordenar ambos em paralelo. Nada exige que vocĂȘ os faça em uma determinada ordem; portanto, sĂŁo tarefas concorrentes. Elas podem ser feitas em qualquer ordem, ou vocĂȘ pode pular para frente e para trĂĄs entre as tarefas atĂ© que ambas sejam feitas; ou, se vocĂȘ tiver os recursos extras (ou talvez alguĂ©m para ajudĂĄ-lo), vocĂȘ pode executĂĄ-las simultaneamente de forma verdadeiramente paralela.

Image description

Isto pode soar estranho: nĂŁo deverĂ­amos dizer que as tarefas sĂł sĂŁo concorrentes se elas estĂŁo acontecendo ao mesmo tempo? Bem, a questĂŁo com essa definição Ă© que elas podem acontecer ao mesmo tempo, e nĂłs somos livres para agendĂĄ-las de acordo com nossa conveniĂȘncia. Tarefas que precisam ser feitas simultaneamente nĂŁo sĂŁo tarefas separadas, enquanto algumas tarefas sĂŁo separadas mas nĂŁo concorrentes e devem ser feitas em ordem, como quebrar o ovo antes de fazer a omelete. As demais sĂŁo concorrentes.

NĂșcleos vs. Threads

Conforme o site da Intel, um thread, ou thread de execução, Ă© um termo de software para a sequĂȘncia bĂĄsica ordenada de instruçÔes que pode ser passada ou processada por um Ășnico nĂșcleo/core de CPU.

  • Single thread: Cada core corresponde Ă  um thread;
  • Multi thread (Hyper-threading): Cada core possui mais de um thread.

Image description

Créditos da imagem acima: https://www.mobilebit.com.br/tecnologia/2020/12/15/threads-o-que-sao-para-que-servem-em-um-processador/

Hyper-threading (threads adicionais)

Image description

  • As Threads adicionais sĂŁo conhecidas popularmente como nĂșcleos lĂłgicos ou nĂșcleos virtuais.
  • As Threads adicionais nĂŁo tem o mesmo poder de processamento de um nĂșcleo fĂ­sico (thread real).
  • As Threads adicionais vĂŁo ajudar em softwares que nĂŁo conseguem lidar com vĂĄrios nĂșcleos fĂ­sicos.

Em um processador de quatro nĂșcleos e oito threads, como Ă© o caso do processador Intel Core i7-1165G7 vocĂȘ verĂĄ em um sistema Linux no Monitor do Sistema oito grĂĄficos representando os nĂșcleos, ou seja, oito linhas de execução (threads) - quatro nĂșcleos fĂ­sicos e quatro nĂșcleos virtuais.

Image description

Veja no site as especificaçÔes do processador Intel Core i7-1165G7
Image description

Formação TS

AlternĂąncia de Tarefas (Task Switching / Context Switching)

Executando mais de uma tarefa ao mesmo tempo, mas nĂŁo de forma paralela.

Image description

AlternĂąncia de tarefas em uma mĂĄquina com um sĂł nĂșcleo e um sĂł thread:
Image description

Historicamente, a maioria dos computadores de mesa (desktop) tiveram um processador, com uma Ășnica unidade de processamento ou nĂșcleo (single core processor). Tal mĂĄquina sĂł pode executar uma tarefa de cada vez, mas pode alternar entre tarefas muitas vezes por segundo. Ao fazer um pouco de uma tarefa e depois um pouco de outra e assim por diante, parece que as tarefas acontecem simultaneamente. Isto chama-se alternĂąncia de tarefas (task switching).

Exemplos de processadores single core:

Execução paralela

Executando mais de uma tarefa ao mesmo tempo e de forma paralela (lado a lado).

Image description

Créditos da imagem acima: https://jenkov.com/.

Computadores contendo mĂșltiplos processadores tĂȘm sido utilizados para servidores e tarefas de computação de alto desempenho durante anos, e computadores baseados em processadores com mais do que um nĂșcleo num Ășnico chip (multicore processors) tornam-se cada vez mais comuns em mĂĄquinas ‘desktop’. Quer tenham processadores mĂșltiplos ou nĂșcleos mĂșltiplos dentro de um processador (ou ambos), estes computadores conseguem executar genuinamente mais do que uma tarefa em paralelo.

Exemplos de processadores multi core:

Alternùncia de Tarefas vs. Execução Paralela

Alternùncia de Tarefas vs. Execução Paralela

Créditos da imagem acima: Livro C++ Concurrency in Action.

GIF Concurrency vs Parallelism

A Figura 1.1 mostra um cenĂĄrio idealizado de um computador com precisamente duas tarefas a fazer, cada uma dividida em 10 blocos de igual tamanho. Numa mĂĄquina de nĂșcleo duplo (com dois nĂșcleos de processamento), cada tarefa pode executar no seu prĂłprio nĂșcleo. Numa mĂĄquina de nĂșcleo Ășnico que faz a troca de tarefas (task switching), os blocos de cada tarefa sĂŁo intercalados. Mas tambĂ©m sĂŁo espaçados um pouco (na figura 1.1, isto Ă© mostrado pelas barras cinzentas que separam os blocos sendo mais espessas do que as barras separadoras mostradas para a mĂĄquina de nĂșcleo duplo); para fazer a intercalação, o sistema tem de executar uma troca de contexto cada vez que muda de uma tarefa para outra, e isto leva tempo. Para executar uma mudança de contexto, o sistema operacional tem de guardar o estado da CPU e o ponteiro de instruçÔes para a tarefa atualmente em execução, determinar para qual tarefa mudar, e recarregar o estado da CPU para a tarefa para a qual Ă© mudada. A CPU terĂĄ entĂŁo potencialmente de carregar a memĂłria para as instruçÔes e os dados para a nova tarefa na memĂłria transitĂłria (cache), o que pode impedir a CPU de executar quaisquer instruçÔes, causando mais atrasos.

Execução paralela e concorrente

Executando mais de uma tarefa ao mesmo tempo mas nĂŁo de forma paralela em cada CPU e as duas CPUs trabalhando de forma paralela.

Em uma CPU acontece a alternĂąncia de tarefas, ou seja, uma tarefa deve parar para a outra prosseguir. As duas CPUs estĂŁo trabalhando em paralelo (lado a lado), ou seja, uma CPU nĂŁo precisar parar para a outra prosseguir.

Image description

Créditos da imagem acima: Livro C++ Concurrency in Action.

Embora a disponibilidade de concorrĂȘncia no hardware seja mais Ăłbvia com sistemas multiprocessadores ou multinĂșcleo, alguns processadores podem executar vĂĄrios fios (threads) num Ășnico nĂșcleo (Hyper-threading). O fator importante a considerar Ă© o nĂșmero de fios do hardware, sendo a medida de quantas tarefas independentes o hardware pode genuinamente executar concorrentemente. Mesmo com um sistema com uma concorrĂȘncia genuĂ­na de hardware, Ă© fĂĄcil ter mais tarefas do que o hardware pode executar em paralelo, por isso a alternĂąncia de tarefas continua a ser utilizada nestes casos. Por exemplo, num computador ‘desktop’ tĂ­pico pode haver centenas de tarefas em execução, executando operaçÔes em segundo plano (background), mesmo quando o computador estĂĄ nominalmente inativo. É a alternĂąncia de tarefas que permite executar estas tarefas em segundo plano e executar o processador de texto, compilador, editor, e browser da web (ou qualquer combinação de aplicaçÔes) tudo de uma sĂł vez. A Figura 1.2 mostra a alternĂąncia de tarefas entre quatro tarefas numa mĂĄquina dual-core, mais uma vez para um cenĂĄrio idealizado com as tarefas divididas ordenadamente em blocos de igual tamanho.

Podemos tambĂ©m ter uma divisĂŁo de uma Ășnica tarefa em subtarefas que podem ser executadas concorrentemente e em paralelo.

Image description

Créditos da imagem acima: https://jenkov.com/.

Conforme o livro C++ Concurrency in Action a concorrĂȘncia e o paralelismo tĂȘm significados amplamente sobrepostos no que diz respeito ao cĂłdigo multithreaded. De fato, para muitos eles significam a mesma coisa. A diferença Ă© principalmente uma questĂŁo de nuance, foco e intenção. Ambos os termos sĂŁo sobre executar mĂșltiplas tarefas simultaneamente, usando o hardware disponĂ­vel, mas o paralelismo Ă© muito mais orientado para o desempenho. As pessoas falam em paralelismo quando sua principal preocupação Ă© aproveitar o hardware disponĂ­vel para aumentar o desempenho do processamento de dados em massa, enquanto as pessoas falam em concorrĂȘncia quando sua principal preocupação Ă© a separação de preocupaçÔes, ou capacidade de resposta.

AssĂ­ncrono X SĂ­ncrono

Podemos encontrar a palavra async (asynchronous = assíncrono) como uma palavra reservada em linguagens de programação para converter código sequencial em código concorrente, como na linguagem Elixir com o Task.async.

Tanto o código Síncrono como Assíncrono são executados aos mesmo tempo, a diferença é que o código assíncrono não hå uma ordem específica e por isso é considerado concorrente:

AssĂ­ncrono = ao mesmo tempo = velocidades diferentes = sem ordem especĂ­fica = concorrente:

Image description

SĂ­ncrono = ao mesmo tempo = velocidades iguais = ordem especĂ­fica = nĂŁo concorrente:

Image description

Image description


ConcorrĂȘncia com mĂșltiplos processos

Image description

Créditos da imagem acima: Livro C++ Concurrency in Action.

A primeira maneira de fazer uso da concorrĂȘncia dentro de uma aplicação Ă© dividir a aplicação em mĂșltiplos processos separados, em uma Ășnica thread, que sĂŁo executados ao mesmo tempo, da mesma forma que vocĂȘ pode executar seu navegador web e processador de texto ao mesmo tempo. Estes processos separados podem entĂŁo passar mensagens uns para os outros atravĂ©s de todos os canais normais de comunicação interprocessados (signals, sockets, files, pipes, etc.), como mostrado na figura 1.3. Uma desvantagem Ă© que tal comunicação entre processos Ă© muitas vezes complicada de configurar ou lenta, ou ambos, porque os sistemas operacionais normalmente fornecem muita proteção entre processos para evitar que um processo modifique acidentalmente os dados pertencentes a outro processo. Outra desvantagem Ă© que hĂĄ uma sobrecarga inerente na execução de mĂșltiplos processos: leva tempo para iniciar um processo, a operação deve dedicar recursos internos ao gerenciamento do processo, e assim por diante.

Nem tudo Ă© negativo: a proteção adicional que os sistemas operacionais normalmente proporcionam entre os processos e os mecanismos de comunicação de nĂ­vel superior significa que pode ser mais fĂĄcil escrever cĂłdigo concorrente seguro com os processos do que com as threads. De fato, ambientes como o fornecido pela linguagem de programação Erlang (www.erlang.org/) utilizam processos como o bloco fundamental da concorrĂȘncia com grande efeito.

A utilização de processos separados para a concorrĂȘncia tambĂ©m tem uma vantagem adicional - vocĂȘ pode executar os processos separados em mĂĄquinas distintas conectadas atravĂ©s de uma rede. Embora isto aumente o custo de comunicação, em um sistema cuidadosamente projetado pode ser uma forma econĂŽmica de aumentar o paralelismo disponĂ­vel e melhorar o desempenho.

Image description

Créditos da imagem acima: Livro Erlang and OTP in Action.

ConcorrĂȘncia com mĂșltiplos threads

Image description

Créditos da imagem acima: Livro C++ Concurrency in Action.

A abordagem alternativa para a concorrĂȘncia Ă© executar vĂĄrios threads em um Ășnico processo. Os threads sĂŁo muito parecidos com processos leves: cada thread funciona independentemente dos outros, e cada um pode executar uma seqĂŒĂȘncia diferente de instruçÔes. Mas todos os threads em um processo compartilham o mesmo espaço de endereço, e a maioria dos dados pode ser acessada diretamente de todos os threads - variĂĄveis globais permanecem globais, e ponteiros ou referĂȘncias a objetos ou dados podem ser passados entre os threads. Embora muitas vezes seja possĂ­vel compartilhar memĂłria entre processos, isto Ă© complicado de configurar e muitas vezes difĂ­cil de gerenciar, porque os endereços de memĂłria dos mesmos dados nĂŁo sĂŁo necessariamente os mesmos em processos diferentes. A Figura 1.4 mostra dois threads dentro de um processo comunicando atravĂ©s da memĂłria compartilhada.

O espaço de endereços compartilhado e a falta de proteção dos dados entre os threads fazem com que a sobrecarga (overhead) associada ao uso de mĂșltiplos threads seja muito menor do que a do uso de mĂșltiplos processos, porque o sistema operacional tem menos contabilidade (bookkeeping) a fazer. Mas a flexibilidade da memĂłria compartilhada tambĂ©m vem com um preço: se os dados sĂŁo acessados por mĂșltiplos threads, o programador da aplicação deve garantir que a visualização dos dados vistos por cada thread seja consistente sempre que ela for acessada. As questĂ”es relacionadas ao compartilhamento de dados entre threads, e as ferramentas a serem usadas e as diretrizes a serem seguidas para evitar problemas sĂŁo abordadas no livro C++ Concurrency in Action. Os problemas nĂŁo sĂŁo intransponĂ­veis, desde que se tome o cuidado adequado ao escrever o cĂłdigo, mas eles significam que muita reflexĂŁo deve ir para a comunicação entre os threads.

A baixa sobrecarga associada ao lançamento e Ă  comunicação entre mĂșltiplos threads dentro de um processo em comparação com o lançamento e a comunicação entre mĂșltiplos processos de uma Ășnica thread significa que esta Ă© a abordagem favorecida para a concorrĂȘncia nas linguagens orientadas a objetos, incluindo C++, apesar dos problemas potenciais decorrentes da memĂłria compartilhada.


ConclusĂŁo

Se eu pudesse resumir concorrĂȘncia em poucas palavras, eu diria que concorrencia Ă© quando duas ou mais atividades independentes acontecem ao mesmo tempo quando nĂŁo hĂĄ uma ordem especĂ­fica. Esse post foi realmente sĂł uma introdução sobre o assunto. HĂĄ muito conhecimento ainda pra ser estudado quando falamos em concorrĂȘncia com mĂșltiplos threads ou concorrĂȘncia com mĂșltiplos processos. Cada abordagem tem seus pontos positivos e negativos e, dependendo da linguagem de programação que vocĂȘ atua; vocĂȘ terĂĄ que focar mais em umas dessas abordagens. O prĂłximo passo agora Ă© procurar colocar a mĂŁo no cĂłdigo visando esses conceitos e como isso pode melhorar e trazer benefĂ­cios ao seu software.

Formação TS

💖 đŸ’Ș 🙅 đŸš©
maiquitome
Dev Maiqui đŸ‡§đŸ‡·

Posted on June 24, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related