Понимание Целлулоидного Параллелизма
Ниже приведены мои целлулоидные коды.
client1.rb
Один из 2 клиентов. (Я назвал его клиентом 1)client2.rb
2-й из 2-х клиентов. (назван как клиент 2)
Замечания:
единственная разница между вышеупомянутыми 2 клиентами - это текст, который передается на сервер. то есть ('client-1'
а также 'client-2'
соответственно)
При тестировании этих 2 клиентов (запустив их рядом) на следующих 2 серверах (по одному за раз). Я нашел очень странные результаты.
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 находился в спящем режиме (знак параллелизма)
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