Во всех ответах в этой теме отсутствует один ключевой момент: ваши обратные вызовы выполняются внутри потока реактора, а не в отдельном отложенном потоке. Выполнение запросов Mechanize при вызове defer
- это правильный способ избежать блокировки цикла, но вы должны быть осторожны, чтобы ваш обратный вызов также не блокировал цикл.
Когда вы запускаете EM.defer operation, callback
, операция запускается в потоке, созданном из Ruby, который выполняет эту работу, а затем в основном цикле выполняется обратный вызов. Следовательно, sleep 1
в operation
выполняется параллельно, а обратный вызов выполняется последовательно . Это объясняет разницу во времени выполнения около 9 секунд.
Вот упрощенная версия кода, который вы используете.
EM.run {
times = 0
work = proc { sleep 1 }
callback = proc {
sleep 1
EM.stop if (times += 1) >= 10
}
10.times { EM.defer work, callback }
}
Это занимает около 12 секунд, что составляет 1 секунду для параллельных снов, 10 секунд для серийных снов и 1 секунду для служебных данных.
Чтобы запустить код обратного вызова параллельно, вы должны породить для него новые потоки, используя прокси-функцию обратного вызова, которая использует EM.defer
, например, так:
EM.run {
times = 0
work = proc { sleep 1 }
callback = proc {
sleep 1
EM.stop if (times += 1) >= 10
}
proxy_callback = proc { EM.defer callback }
10.times { EM.defer work, proxy_callback }
}
Однако с этим вы можете столкнуться с проблемами, если ваш обратный вызов должен затем выполнять код в цикле событий, потому что он запускается в отдельном, отложенном потоке. Если это произойдет, переместите код проблемы в функцию обратного вызова процедуры proxy_callback.
EM.run {
times = 0
work = proc { sleep 1 }
callback = proc {
sleep 1
EM.stop_event_loop if (times += 1) >= 5
}
proxy_callback = proc { EM.defer callback, proc { "do_eventmachine_stuff" } }
10.times { EM.defer work, proxy_callback }
}
Эта версия работала примерно за 3 секунды, что составляет 1 секунду ожидания для параллельной работы, 1 секунду ожидания для обратного вызова в параллельном и 1 секунду для служебных данных.