Ruby 1.9 com threads não bloqueantes
Publicado/atualizado em 20/11/2007 11:20
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.
Permalink: http://eustaquiorangel.com/posts/481
salvar no del.icio.ussalvar no diggsalvar no rec6 Veja o que estão dizendo sobre isso.Comente
Linhas em branco viram saltos de linha. Se você quiser mostrar algum código, por favor use o pastebin e informe a URL.
Comentários
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. :-)






