Понимание Целлулоидного Параллелизма

Ниже приведены мои целлулоидные коды.

  1. client1.rb Один из 2 клиентов. (Я назвал его клиентом 1)

  2. client2.rb 2-й из 2-х клиентов. (назван как клиент 2)

Замечания:

единственная разница между вышеупомянутыми 2 клиентами - это текст, который передается на сервер. то есть ('client-1' а также 'client-2' соответственно)

При тестировании этих 2 клиентов (запустив их рядом) на следующих 2 серверах (по одному за раз). Я нашел очень странные результаты.

  1. server1.rb (основной пример взят из README.md целлулоида-zmq)

    Использование этого сервера в качестве примера для двух вышеуказанных клиентов привело к параллельному выполнению задач.

ВЫХОД

ruby server1.rb

Received at 04:59:39 PM and message is client-1
Going to sleep now
Received at 04:59:52 PM and message is client-2

Замечания:

сообщение client2.rb было обработано, когда запрос client1.rb находился в спящем режиме (знак параллелизма)

  1. server2.rb

    Использование этого сервера в качестве примера для двух вышеперечисленных клиентов не привело к параллельному выполнению задач.

ВЫХОД

ruby server2.rb

Received at 04:55:52 PM and message is client-1
Going to sleep now
Received at 04:56:52 PM and message is client-2

Замечания:

клиент-2 попросили подождать 60 секунд, так как клиент-1 спал (60 секунд спит)

Я запускал вышеупомянутый тест несколько раз, и все приводило к одинаковому поведению

Может ли кто-нибудь объяснить мне по результатам вышеуказанных тестов, что.

Вопрос: почему целлулоид вынужден ждать 60 секунд, прежде чем он сможет обработать другой запрос, то есть, как было отмечено в случае server2.rb.?

Рубиновая версия

ruby -v

ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]

2 ответа

Решение

Используя ваши суть, я убедился, что эту проблему можно воспроизвести в MRI 2.2.1 так же как jRuby 1.7.21 а также Rubinius 2.5.8... Разница между server1.rb а также server2.rb это использование DisplayMessage а также message метод класса в последнем.


Использование sleep в DisplayMessage вне Celluloid объем.

когда sleep используется в server1.rb это использует Celluloid.sleep на самом деле, но при использовании в server2.rb это использует Kernel.sleep... который блокирует почтовый ящик для Server пока не прошло 60 секунд. Это предотвращает будущие вызовы методов для этого субъекта до тех пор, пока почтовый ящик снова не обработает сообщения (вызовы методов для субъекта).

Есть три способа решить эту проблему:

  • Использовать defer {} или же future {} блок.

  • Явное обращение Celluloid.sleep скорее, чем sleep (если явно не вызывается как Celluloid.sleep, с помощью sleep в конечном итоге вызов Kernel.sleep поскольку DisplayMessage не include Celluloid лайк Server делает)

  • Принесите содержимое DisplayMessage.message в handle_message как в server1.rb; или, по крайней мере, в Server, который в Celluloid сфера, и будет использовать правильный sleep,


defer {} подход:

def handle_message(message)
  defer {
    DisplayMessage.message(message)
  }
end

Celluloid.sleep подход:

class DisplayMessage
    def self.message(message)
      #de ...
      Celluloid.sleep 60
    end
end

На самом деле проблема не в объеме; это об асинхронности.

Повторюсь, более глубокий вопрос не в сфере sleep... вот почему defer а также future мои лучшие рекомендации. Но чтобы опубликовать кое-что здесь, что вышло в моих комментариях:

С помощью defer или же future выдвигает задачу, из-за которой актер становится связанным с другим потоком. Если вы используете future, вы можете получить возвращаемое значение после выполнения задачи, если вы используете defer ты можешь выстрелить и забыть.

Но еще лучше, создайте другого актера для задач, которые, как правило, связаны, и даже объедините этого другого актера... если defer или же future не работает для вас.

Я был бы более чем счастлив ответить на последующие вопросы, поднятые этим вопросом; у нас очень активный список рассылки и IRC канал. Ваши щедрые награды достойны похвалы, но многие из нас могут помочь вам.

Удалось воспроизвести и исправить проблему. Удаление моего предыдущего ответа. Видимо, проблема заключается в sleep , Подтверждено добавлением логов "actor/kernel sleeping" в локальную копию Celluloids.rb sleep() ,


В server1.rb,

призыв к sleep внутри server - класс, который включает целлулоид.

Таким образом, реализация целлулоида sleep переопределяет родной sleep ,

class Server
  include Celluloid::ZMQ

  ...

  def run
    loop { async.handle_message @socket.read }
  end

  def handle_message(message)

        ...

        sleep 60
  end
end

Обратите внимание на журнал actor sleeping от server1.rb , Лог добавлен в Celluloids.rb sleep()

Это приостанавливает только текущий "субъект" в Celluloid, т.е. только текущий "Celluloid thread", обрабатывающий client1, спит.


В server2.rb,

призыв к sleep находится в другом классе DisplayMessage это не включает целлулоид.

Таким образом, это родной sleep сам.

class DisplayMessage
    def self.message(message)

           ...

           sleep 60
    end
end

Обратите внимание на ОТСУТСТВИЕ любого actor sleeping войти из server2.rb ,

Это приостанавливает текущую задачу ruby, т. Е. Сервер ruby ​​спит (а не один актер Celluloid).


Исправление?

В server2.rb , соответствующий sleep должен быть явно указан.

class DisplayMessage
    def self.message(message)
        puts "Received at #{Time.now.strftime('%I:%M:%S %p')} and message is #{message}"
        ## Intentionally added sleep to test whether Celluloid block the main process for 60 seconds or not.
        if message == 'client-1'
           puts 'Going to sleep now'.red

           # "sleep 60" will invoke the native sleep.
           # Use Celluloid.sleep to support concurrent execution
           Celluloid.sleep 60
        end
    end
end
Другие вопросы по тегам