Ruby Threading не будет переключать контекст

#ruby #alsa

#ruby #alsa

Вопрос:

Используя MRI ruby 1.9, у меня есть некоторый код, подобный

 def foo()
  puts "in foo"
  loop do
    puts "in foo loop"
  end
end

def bar()
  puts "in bar"
  start_alsa_listener
end

foo_thread = Thread.new { foo }
bar_thread = Thread.new { bar }
foo_thread.join
bar_thread.join
  

start_alsa_listener — это блокирующий вызов библиотеки, который открывает ALSA midi sequencer и ожидает событий ввода в нем. По сути, я хочу, чтобы мой код постоянно выводился «в цикле foo» и в то же время мог получать события ALSA midi и также выводить их на консоль (что start_alsa_listener и делает, когда получает событие).

Проблема в том, что когда я запускаю приведенный выше код, как только bar () запускает его, контекст никогда не переключается обратно на foo ().

start_alsa_listener — это расширение ruby C, которое выглядит как:

 for(;;) {
    poll(/* args */);      /* wait for input data */
    /* print data to console */
}
  

Возможно, это связано с чем-то в Ruby, что я делаю неправильно с потоковой обработкой, или, может быть, что-то связано с опросом, или, может быть, что-то связано с тем, как ALSA обрабатывает потоки. Приветствуется любая помощь.

Ответ №1:

Показанный вами цикл заблокирует весь интерпретатор (при условии, что poll он блокируется), как сказал Стив. Вам нужно вызвать poll(), используя rb_thread_blocking_region из Ruby (MRI / YARV) C API.

Ответ №2:

Без cext эти два работают параллельно. GIL не позволяет запускать два потока одновременно с расширением C, потому что он не может знать, что это потокобезопасно.

Комментарии:

1. им не нужно работать параллельно как таковым, если они включают и выключают контекст, чтобы создавалось впечатление, что они работают параллельно. Делает ли ruby что-нибудь, чтобы предотвратить даже это?

2. ДА. Как я уже сказал, расширение C означает, что GIL удерживается потоком с расширением c, что означает, что другие потоки не могут запускаться.