Предполагая, что ваша проблема является медленным внешним API, решением может быть использование либо потокового программирования, либо асинхронного программирования. По умолчанию при выполнении ввода-вывода ваш код блокируется. Это в основном означает, что если у вас есть метод, который выполняет HTTP-запрос для получения некоторого JSON, ваш метод скажет вашей операционной системе, что вы собираетесь спать, и вы не хотите, чтобы вас разбудили, пока операционная система не ответит на этот запрос. Поскольку это может занять несколько секунд, ваше приложение просто будет ждать.
Это поведение не относится только к HTTP-запросам. Чтение из файла или устройства, такого как веб-камера, имеет те же последствия. Программное обеспечение делает это, чтобы предотвратить зависание процессора, когда он, очевидно, не использует его.
Итак, вопрос в вашем случае: действительно ли нам нужно дождаться завершения одного метода, прежде чем мы сможем позвонить другому? В случае, если поведение method_two
зависит от результата method_one
, тогда да. Но в вашем случае кажется, что это отдельные единицы работы без сопричастности. Таким образом, существует возможность для выполнения параллелизма.
Вы можете создавать новые потоки, инициализируя экземпляр класса Thread с блоком, который содержит код, который вы хотите запустить. Подумайте о потоке как программе внутри вашей программы. Ваш интерпретатор Ruby будет автоматически чередоваться между потоком и основной программой. Вы можете запустить столько потоков, сколько захотите, но чем больше потоков вы создадите, тем дольше ваша основная программа должна будет подождать, прежде чем вернуться к исполнению. Однако мы, вероятно, говорим о микросекундах или меньше. Давайте рассмотрим пример выполнения потоковой передачи.
def main_method
Thread.new { method_one }
Thread.new { method_two }
Thread.new { method_three }
end
def method_one
# something_slow_that_does_an_http_request
end
def method_two
# something_slow_that_does_an_http_request
end
def method_three
# something_slow_that_does_an_http_request
end
Вызов main_method
заставит все три метода, которые будут выполняться в том, что, как представляется, параллельны друг другу. В действительности они по-прежнему обрабатываются последовательно, но вместо того, чтобы спать, когда блоки method_one
, Ruby просто вернется в основной поток и вернется к method_one
потоку, когда ОС готова.
Предполагая, что для каждого метода требуется два 2 мс, минуя ожидание ответа, это означает, что все три метода работают всего через 6 мс - практически мгновенно.
Если мы предположим, что ответ займет 500 мс, это означает, что вы можете сократить общее время выполнения от 2 + 500 + 2 + 500 + 2 + 500 до 2 + 2 + 2 + 500 - в другом слова от 1506 мс до 506 мс.
Чувствуется, что методы работают одновременно, но на самом деле они просто спят одновременно.
В вашем случае, однако, у вас есть вызов, потому что у вас есть операция, которая зависит от завершения набора предыдущих операций. Другими словами, если у вас есть задачи A, B, C, D, E и F, то A, B, C, D и E могут выполняться одновременно, но F не может выполняться до тех пор, пока A, B, C, D и E все они полны.
Существуют различные способы решения этой проблемы. Давайте посмотрим на простое решение, которое создает сонный цикл в основном потоке, который периодически проверяет список возвращаемых значений, чтобы убедиться, что какое-то условие заполнено.
def task_1
# Something slow
return results
end
def task_2
# Something slow
return results
end
def task_3
# Something slow
return results
end
my_responses = {}
Thread.new { my_responses[:result_1] = task_1 }
Thread.new { my_responses[:result_2] = task_2 }
Thread.new { my_responses[:result_3] = task_3 }
while (my_responses.count < 3) # Prevents the main thread from continuing until the three spawned threads are done and have dumped their results in the hash.
sleep(0.1) # This will cause the main thread to sleep for 100 ms between each check. Without it, you will end up checking the response count thousands of times pr. second which is most likely unnecessary.
end
# Any code at this line will not execute until all three results are collected.
Имейте в виду, что многопоточное программирование представляет собой сложную тему с многочисленными подводными камнями. С MRI это не так уж плохо, потому что, хотя MRI будет успешно переключаться между заблокированными потоками, MRI не поддерживает одновременное выполнение двух потоков, и это решает довольно много проблем с параллелизмом.
Если вы хотите попасть в многопоточное программирование, я рекомендую эту книгу: http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601
Это сосредоточенное вокруг Java, но подводные камни и понятия объясняется являются универсальными.
Вы хотите http://celluloid.io/, я думаю –