Blog

LISTAR TODOS OS POSTS - Assine os feeds dos posts e comentários

Ruby 1.9 com threads não bloqueantes

Publicado/atualizado em 20/11/2007 11:20

Ruby

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.

*

*

Responda: Água mole em pedra dura tanto bate até que ...?  
Clique aqui se não souber essa resposta!

* campos obrigatórios

Comentários

1 - Diogenes disse em 27/11/2007 17:57

TaQ,

Jah conseguiu replicar isso sem usar FIFOs?

[]'s


2 - TaQ disse em 28/11/2007 09:34

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. :-)


Anterior Próximo Últimos Índice