Ruby 1.9 com threads não bloqueantes
Publicado em Developer
![Ruby](/images/rubylogo.gif)
Eu estou sempre de olho em algumas coisinhas novas implementadas no Ruby 1.9 (inclusive vou falar de algumas delas no MinasOnRails, apareça lá para comer um pão-de-queijo com a gente!) e uma que eu estava de olho era na questão de que se uma thread que fosse bloqueada através de algum recurso do sistema mais chatinho, bloquearia a execução das outras. Isso definitivamente não é uma coisa legal, e como o anúncio do Ruby 1.9 com native threads para o Natal desse ano eu decidi rodar um teste aqui para ver como elas estavam se comportando.
O teste é simples e consiste em fazer duas threads ler dois arquivos texto enquanto outra lê um FIFO. Para criar os arquivos e o FIFO podemos fazer dessa maneira:
echo "thread_test file" > thread_test.txt echo "thread_test2 file" > thread2_test.txt mkfifo thread_test.fifo
Nesse ponto temos nossos dois arquivos, com conteúdo mínimo e um FIFO vazio. Fazendo um teste simples no FIFO, abra dois terminais no mesmo diretório e digite os seguintes comandos, cada um em um terminal:
tail -f thread_test.fifo echo "oi" > thread_test.fifo
Como podemos ver, é mostrado o conteúdo e esvaziado o FIFO com o tail. É esse o ponto onde ocorre o bloqueio. Vamos rodar o seguinte código, primeiro na versão atual do Ruby, que usa green threads:
1 files = %w(thread_test.txt thread_test.fifo thread_test2.txt) 2 threads = [] 3 4 files.each_with_index do |item,index| 5 threads << Thread.new do 6 while true 7 puts "(#{index}) Reading #{item} on #{Time.now} ..." 8 puts File.read(item) 9 puts "-"*50 10 sleep 1 11 end 12 end 13 end 14 threads.first.join
ruby thread_test.rb (0) Reading thread_test.txt on Tue Nov 20 10:41:52 -0200 2007 ... (1) Reading thread_test.fifo on Tue Nov 20 10:41:52 -0200 2007 ...
Oh-oh. Se não interrompermos o processamento, o programa vai ficar parado ali para sempre. Como podemos ver, o número 1 ali é a thread que lê o FIFO, e como não há conteúdo, ela fica parada bloqueando as outras. Argh. Se enviarmos conteúdo com um echo para o FIFO de maneira similar à mostrada acima, o bloqueio é removido somente até a thread rodar novamente. Nada bom. Inclusive, para interromper o processamento é necessário dar CTRL+C e enviar alguma coisa para o FIFO, senão só fechando o terminal.
Mas seus problemas acabaram! Com o Ruby 1.9 e a correção que o Koichi fez no último commit, rodando o código vamos ter:
ruby1.9 thread_test.rb (0) Reading thread_test.txt on 2007-11-20 10:47:00 -0200 ... thread_test file -------------------------------------------------- (1) Reading thread_test.fifo on 2007-11-20 10:47:00 -0200 ... (2) Reading thread_test2.txt on 2007-11-20 10:47:00 -0200 ... thread_test2 file -------------------------------------------------- (0) Reading thread_test.txt on 2007-11-20 10:47:01 -0200 ... thread_test file -------------------------------------------------- (2) Reading thread_test2.txt on 2007-11-20 10:47:01 -0200 ... thread_test2 file -------------------------------------------------- oi -------------------------------------------------- (0) Reading thread_test.txt on 2007-11-20 10:47:02 -0200 ... thread_test file -------------------------------------------------- (2) Reading thread_test2.txt on 2007-11-20 10:47:02 -0200 ... thread_test2 file -------------------------------------------------- (1) Reading thread_test.fifo on 2007-11-20 10:47:03 -0200 ... (0) Reading thread_test.txt on 2007-11-20 10:47:03 -0200 ... thread_test file -------------------------------------------------- (2) Reading thread_test2.txt on 2007-11-20 10:47:03 -0200 ... thread_test2 file -------------------------------------------------- thread_test.rb:14:in `join': Interrupt from thread_test.rb:14:in `'
Como podemos ver, sem bloqueios agora. A thread foi ler o FIFO (1), não encontrou nada, ficou esperando mas sem bloquear as outras. Somente depois que os arquivos foram lidos duas vezes eu enviei o "oi" para o FIFO, que pode ser visto em uma linha ali em cima sozinho. Segundo o Koichi disse, ele modificou o Global VM Lock (GVL, o terror de muita gente) e quer revisar a IO do Ruby com as natives threads, será que dá tempo até o Natal? Se não der não me culpem! :-D
Para finalizar, o Charles Nutter comentou aqui que esse tipo de comportamento é possível com a versão atual do Ruby, mas já que o 1.9 está chegando com todo o conceito novo das native threads (mesmo que ainda use alguns conceitos anteriores como o global lock) acredito que esse fix do Koichi só deva aparecer nessa versão vindoura mesmo. Tomara que a revisão mencionada pelo Koichi consiga agradar a Gregos e Troianos, tem uma turma meio brava e desapontada com os rumos da coisa.
Comentários
Comentários fechados.
Artigos anteriores
- Pull requests em modo raiz - sex, 22 de dezembro de 2023, 09:57:09 -0300
- Qual a idade do seu repositório? - ter, 27 de dezembro de 2022, 12:50:35 -0300
- Utilizando ctags em projetos Rails mais recentes - qui, 24 de junho de 2021, 08:23:43 -0300
- Fazendo o seu projeto brotar - seg, 15 de julho de 2019, 08:57:05 -0300
- Learn Functional Programming with Elixir - sex, 02 de março de 2018, 18:47:13 -0300
- Ambiente mínimo - Driver Driven Development - qua, 23 de agosto de 2017, 15:15:03 -0300
- Ambiente mínimo - repositórios de código - dom, 16 de abril de 2017, 13:02:14 -0300
- Ambiente mínimo - terminal e navegador - dom, 02 de abril de 2017, 21:43:29 -0300
- Utilizando muitas gems no seu projeto? - sáb, 29 de outubro de 2016, 11:57:55 -0200
- Desenvolvedores e inteligência artificial - seg, 11 de julho de 2016, 09:09:38 -0300
Diogenes, o jeito mais "chatinho" e que eu fiz as threads "gritarem" ali foi com os FIFOs, com outros meios de IO elas se comportaram até bem. Aí eu fiquei fuçando até achar um que chiava. :-)
TaQ,
Jah conseguiu replicar isso sem usar FIFOs?
[]'s