Descubra o poder da programação concorrente com a linguagem Go

O mundo da tecnologia se encontra em uma constante corrida: a corrida das tecnologias,das ferramentas, das linguagens de programação. Novos problemas vão surgindo e, com eles, novas demandas, novas formas de resolvê-los. Às vezes, até mesmo velhos problemas são surpreendidos com novas ferramentas. E, assim, vamos caminhando em busca da tecnologia perfeita.

Uma das demandas recorrentes no domínio do desenvolvimento de software é a questão da performance. A cada geração que passa, nossas peças físicas – o hardware – se tornam mais robustas. Placas de vídeo, processadores com múltiplos núcleos, memória… Mas algumas das nossas linguagens de programação não conseguem acompanhar em tempo real, ou simplesmente não têm a estrutura necessária para fazer um uso ótimo desses recursos. Daí nascem novas linguagens.

Processadores, núcleos, paralelismo, concorrência… Quê?

Com processadores que contam com cada vez mais núcleos e, portanto, poder de processamento, é mais do que desejável que nossos programas consigam usar tudo o que há disponível para maior velocidade e performance e, com isso, tornar a experiência do usuário melhor. Em pleno 2022, nós humanos queremos as informações rapidamente e nossos sistemas ágeis, e é por isso que nós desenvolvedores fazemos uso pesado de paralelismo e concorrência onde é possível e viável. Mas… O que são essas coisas, afinal de contas?

Vamos falar sobre café.

A grande maioria dos programadores tem algo com café. De dez desenvolvedores que eu conheço, nove bebem café; o outro bebe alguma outra bebida com cafeína, como energéticos. Se você se encaixa no grupo dos que tomam café, muito provavelmente você sabe fazer o sua própria bebida. Então vamos elaborar alguns cenários a seguir.

Cenário A: Eu começo pegando um copo e colocando-o na pia. Encho-o com água e despejo a mesma dentro da minha cafeteira. Pego um filtro de papel, o coloco na cafeteira e jogo três colheres de café em pó nele. Ligo a cafeteira, espero o café coar e, depois, me sirvo despejando o líquido divino no copo. Então,bebo o café.

Cenário B: Após convidar e servir a minha irmã com um belo almoço em casa, pergunto se ela aceita um café. Ela gosta tanto quanto eu do néctar dos deuses, então aceita. A primeira coisa a fazer é encher o fervedor com água e colocá-lo sobre uma das bocas do fogão já acesas. A seguir, eu pego o meu coador, coloco e ajusto um filtro de papel nele e ponho pó de café. Cerca de quatro colheres. Enquanto a água começa a ferver, eu arrumo o que dá na mesa. Com a água fervida e o coador posicionado sobre uma jarra, despejo a água sobre o café lentamente em movimentos circulares, usando cada grão visível. Já é possível sentir o cheiro magnífico de café fresquinho. No que o líquido escoa para a jarra, pego dois copos. Em um deles coloco uma colher de açúcar. Eu prefiro puro, então não coloco no meu. O café termina de coar e eu o sirvo nos dois copos. Bebemos o café.

Cenário C: A hora da janta sempre conta com movimento intenso no restaurante. Eu cuido de fazer os espressos junto com colegas meus. Como bons apreciadores, sempre tentamos fazer o melhor café possível. Normalmente eu opero a máquina: carrego-a com os grãos e cuido para que sempre haja água fresca. Enquanto preparo duas xícaras, um dos meus colegas enche dois copinhos com água com gás e separa o doce extra que vai junto. Um terceiro colega coloca tudo em uma bandeja e sai em direção à mesa de nossos clientes. E assim entregamos mais um espresso enquanto chega outro pedido.

Ufa! Quanto café. Depois disso tudo, precisamos tomar cuidado para não desenvolver ou desencadear uma crise de gastrite. Atenção à quantidade de café que você toma. 🙂

Nos três cenários temos aplicações diferentes de como fazer café. O cenário A conta com execução sequencial, em que fazemos instrução a instrução, esperando cada uma delas terminar por completo antes de começar a próxima.
Já o cenário B conta com execução concorrente: não precisamos esperar o café terminar de coar para só então pegar os copos e servir. Podemos preparar o café enquanto a água ferve, e preparamos os copos enquanto o café termina de coar. Sabemos que cada uma dessas tarefas leva um tempo até que sejam concluídas, então agilizamos o processo preparando outras coisas em vez de esperar ociosamente, como é o caso do algoritmo sequencial do cenário A.

O cenário C, como você já deve ter previsto, é o que faz uso da execução paralela. O fato de termos pessoas diferentes fazendo tarefas diferentes ajuda e muito na performance do algoritmo. Claro que é um pouco mais difícil de gerenciar (e depurar) quando se trata de código, mas a ideia de que é mais eficiente já deve estar clara agora. Com um processador com múltiplos núcleos é possível dividir as tarefas em threads ou processos e juntar os pedaços do resultado no final.

Certo, e onde queremos chegar com tudo isso?

Algoritmos paralelos não são necessariamente novidade. Desde C já era possível criar threads e controlar (de certa forma) o fluxo por meio de mutexes. A novidade, no caso, é a concorrência: nos últimos anos, muitas linguagens de programação vêm adotando a concorrência de algumas formas. Algumas fazem uso de Promises ou Futures, estruturas para as quais passamos callbacks que são executados assincronamente; outras adotaram novas palavras-chave, como async e await para definir funções assíncronas e ordenar o programa que aguarde certas computações, respectivamente. Algumas adotaram ambas as formas. E há também a linguagem Go.

Já ouvi falar. Essa é a linguagem da Google, certo? Golang?

Essa mesma! A linguagem Go nasceu com concorrência em mente e possui uma forma muito facilitada de lidar com entrada e saída assíncrona. Programas concorrentes fazem uso de algo que chamamos de event loop. Esses event loops gerenciam qual pedaço de código deve ser executado em que instante. Go abstrai isso de forma que todos os programam rodam, por padrão, em um event loop. Todas as funções são assíncronas, e todas podem ser usadas de forma assíncrona ou não. Por exemplo, para somar dois números:

Essa é a forma como estamos acostumados a escrever código síncrono. Mas podemos chamar a função assincronamente colocando um simples go à frente dela:

* Um chan(channel) funciona como uma fila assíncrona e possibilita a passagem de mensagens entre as goroutines – as co-rotinas do Go.

Ainda que este exemplo seja simples, talvez seja possível ter uma ideia de o que é possível fazer com a linguagem. Com concorrência facilitada dessa forma, processamento assíncrono se torna algo comum e rotineiro no desenvolvimento. E essa é só uma das características legais e interessantes da linguagem! Nós falamos mais a fundo sobre essa e outras funcionalidades de Go no novo curso que lançaremos em breve de Desenvolvimento com Golang na 4Linux. Se você leu até aqui, é porque deve estar interessado. Então por que não conhecer um pouco mais do curso? Vem aprender Golang com a gente! 🙂

Líder em Treinamento e serviços de Consultoria, Suporte e Implantação para o mundo open source. Conheça nossas soluções:

CURSOSCONSULTORIA

Anterior Como criar uma federação de usuários com Keycloak e LDAP: um tutorial passo a passo
Próxima Entenda a interação entre MySQL e o cache de sistema de arquivos do Linux

About author

Você pode gostar também

Infraestrutura TI

Vagrant: o que é, onde vive e o que come? Aqui no blog da 4Linux!

A tecnologia nunca foi algo provisório ou estacionário, a todo momento vemos mudanças e essas são sempre acompanhadas de desafios. Estar antenado com atualizações e lançamentos é uma tarefa árdua,

Desenvolvimento

Organize seus objetos de banco de dados com schemas PostgreSQL no Django

Que tal se você pudesse organizar seus objetos de bancos de dados (suas tabelas, views, functions, procedures etc.) em namespaces de acordo com suas respectivas funções no sistema? Neste artigo

Treinamentos

Cursos de TI gratuitos: Capacite-se para o mercado em crescimento

Cursos de TI gratuitos Segundo a Associação Brasileira das Empresas de Tecnologia da Informação e Comunicação (Brasscom), o setor de TI e comunicação representa 8,8% do produto interno bruto (PIB)