Волокно в EM: соединение (em-синхрония)
Кто-нибудь может мне объяснить, почему драйвер синхронизации Redis (redis-rb) работает непосредственно в блоке EM.synchrony, но не в EM:Connection?
Учитывая следующий пример
EM.synchrony do
redis = Redis.new(:path => "/usr/local/var/redis.sock")
id = redis.incr "local:id_counter"
puts id
EM.start_server('0.0.0.0', 9999) do |c|
def c.receive_data(data)
redis = Redis.new(:path => "/usr/local/var/redis.sock")
puts redis.incr "local:id_counter"
end
end
end
я собираюсь
can't yield from root fiber (FiberError)
при использовании внутри receive_data
, Из чтения исходного кода для EventMachine и em-synchrony я не могу понять, в чем разница.
Спасибо!
PS: Очевидный обходной путь - обернуть код redis в EventMachine::Synchrony.next_tick, как было указано в выпуске № 59, но, учитывая EM.synchrony, я ожидаю, что вызов уже будет заключен в Fiber...
PPS: то же самое относится к использованию EM::Synchrony::Iterator
1 ответ
Здесь вы делаете довольно сложные вещи. Вы предоставляете блок для start_server, который эффективно создает "анонимный" класс соединения и выполняет ваш блок в методе post_init этого класса. Затем в этом классе вы определяете метод экземпляра.
Следует помнить следующее: когда реактор выполняет обратный вызов или метод, такой как receive_data, это происходит в основном потоке (и внутри корневого волокна), поэтому вы видите это исключение. Чтобы обойти это, вам нужно обернуть каждый обратный вызов, который будет выполнен в Fiber (например, см. Synchrony.add_(периодический)_timer методы).
Для устранения вашего фактического исключения: оберните выполнение receive_data в Fiber. Внешний EM.synchrony {} ничего не сделает для обратных вызовов, которые запланированы позже реактором.