Волокно в 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 {} ничего не сделает для обратных вызовов, которые запланированы позже реактором.

Другие вопросы по тегам