Green threads

Quando falamos de threads, geralmente estamos nos referindo a threads do sistema operacional, ou kernel threads. Algumas implementações de linguagens de programação, como o GCC em C, permitem utilizar kernel threads, que no caso do C é através da função pthread_create .

Mas como vimos no tópico sobre os desafios com o uso de kernel threads, a gestão de milhares de threads pode aumentar consideravelmente a latência do sistema.

Como alternativa, algumas implementações optam por usar abstrações similar a threads dentro do próprio runtime, pelo que precisam desenvolver o próprio escalonador de threads.

A essas threads que vivem dentro do runtime, ou como costumamos dizer, a nível do usuário, damos o nome de green threads.

Mas antes de falarmos das green threads, vamos relembrar o funcionamento de uma kernel thread.

Funcionamento de uma kernel thread

Como já aprendemos nos tópicos anteriores, em C conseguimos criar threads do sistema operacional, ou kernel threads. Basicamente, uma kernel thread tem uma pilha onde guarda o estado de execução entre outros metadados:

Em C, a chamada da função pthread_create basicamente cria uma kernel thread, então a gestão de escalonamento e troca de contexto fica completamente a cargo do kernel (SO), e não do GCC:

Como funciona uma green thread

Uma green thread geralmente tem uma estrutura similar a uma kernel thread, contendo sua própria pilha de memória mas compartilhando a memória principal do processo.

Mas uma green thread tem a vantagem de não levar com a latência da criação de kernel threads, ou seja, dependendo da implementação de green threads no runtime, podemos criar milhares, senão milhões de green threads mantendo baixa latência.

A nível de implementação, em determinado momento vamos precisar "associar" as green threads às kernel threads, afinal:

Todo programa roda em uma thread principal, ou seja, para o escalonador do sistema operacional é tudo thread, ou melhor ainda, é tudo task

O desafio é então multiplexar um número arbitrário de green threads para kernel threads, e isto pode ser feito de várias formas. Vamos a seguir detalhar alguns tipos comuns de implementação de green threads.

1. Uma kernel thread, múltiplas green threads

Neste tipo de implementação, temos apenas uma thread principal do programa que é mapeada diretamente para uma kernel thread. E dentro desta thread, criamos múltiplas green threads:

A vantagem desta abordagem é que temos menos latência comparando com o uso indiscriminado de kernel threads, mas por outro lado, a desvantagem é que não temos um paralelismo real mesmo em CPU multi-core, pois o sistema operacional não sabe que se trata de uma green thread, então no fim das contas é apenas uma thread sendo utilizada na CPU.

2. Multiplexação de N green threads para M kernel threads

Esta abordagem é muito comum no runtime Go, e é basicamente a capacidade de criar uma kernel thread para um conjunto específico de green threads:

A vantagem nisso é que podemos utilizar mais paralelismo, uma vez que cada kernel thread pode utilizar um núcleo de CPU. Se o runtime for espertinho o suficiente, temos uma situação muito interessante para lidar com concorrência e paralelismo.

Alô Gophers, o momento de vocês está chegando :P

3. Múltiplos escalonadores

Outra abordagem muito interessante, e que é utilizada na implementação do Erlang/Elixir (BEAM), é que poderíamos multiplexar kernel threads pra cada escalonador. Assim, cada escalonador iria cuidar de um número X de green threads mantendo o paralelismo real.

Mas a ideia por enquanto não é falar de Go nem Erlang, pois estamos ainda no C. Chegou o momento de entender como podemos trabalhar com green threads em C.

Green threads em C

Infelizmente, a implementação padrão do GCC não traz suporte a green threads. O que até faz sentido, imagina o C trazer isso? Seria overkill, sendo uma linguagem bastante genérica, de propósito geral e muito perto do sistema operacional.

Mas há algumas bibliotecas externas que trazem este conceito, onde podemos ter "green threads" em C contando com uma estrutura de escalonamento das threads:

  • libdill.org: green threads (chamadas na lib de corrotinas), com comunicação baseada em canais e escalonamento cooperativo

  • libmill.org: similar à libdill, mas implementada de forma mais robusta e escalável, com multiplexing de kernel threads semelhante ao que temos em Go

  • ...entre outras, como libtask, libcoro, GNU pth etc

Last updated