В обоих случаях первое значение было отправлено, как и следовало ожидать. Однако два типа каналов начинают различаться, когда отправляется второе значение. В случае без буферизации ожидающий получатель отсутствует, поэтому канал запускает перепланирование, в результате чего выполнение переключается обратно на основное волокно. После печати первых двух значений выполнение переключается обратно на волокно и отправляет третье значение. Это приводит к перепланированию, при котором выполнение будет переключено обратно на основное волокно, когда в следующий раз появится такая возможность. В данном конкретном случае такой шанс появляется после печати конечного сообщения и когда в волокне больше нечего выполнять.
В случае с буферизацией первое отправленное значение выполняет команду channel.receive
puts “Before send 4”
и channel.send 4
перед конечным сообщением. Затем обновите цикл, чтобы сказать 4.times do
. Повторный запуск программы дает следующий результат:Before send 1
Before send 2
Before send 3
Before send 4
1
2
3
4
Обратите внимание, что на этот раз конечное сообщение не было напечатано. Это связано с тем, что второе и третье значения укладываются в размер буфера, равный 2. Однако, когда отправляется четвертое значение, буфер больше не может обрабатывать дополнительные значения, поэтому канал запускает перепланирование, в результате чего выполнение переключается на основное волокно снова. Поскольку первое значение было отправлено как часть исходного канала channel.recieve
Во всех этих примерах мы получали значение из одного канала.
select
(не путать с методом #select
). Ключевое слово select
позволяет вам ожидать на нескольких каналах и выполнять некоторую логику в зависимости от того, какой из них получит значение первым. Кроме того, он поддерживает работу логики, если все каналы заблокированы и по истечении заданного периода времени значение не получено. Начнем с простого примера:channel1 = Channel(Int32).new
channel2 = Channel(Int32).new
spawn do
puts "Starting fiber 1"
sleep 3
channel1.send 1
end
spawn do
puts "Starting fiber 2"
sleep 1
channel2.send 2
end
select
when v = channel1.receive
puts "Received #{v} from channel1"
when v = channel2.receive
puts "Received #{v} from channel2"
end
Этот пример выводит следующее:
Starting fiber 1
Starting fiber 2
Received 2 from channel2
Здесь оба волокна начинают выполняться более или менее одновременно, но поскольку у второго волокна более короткий период сна и он завершается первым, это приводит к тому, что ключевое слово select
select
действует аналогично одиночному каналу channel.receive
в том смысле, что оно блокирует основное волокно, а затем продолжает работу после получения значения из любого канала. Кроме того, мы могли бы обрабатывать несколько итераций, поместив ключевое слово select
в цикл вместе с методом timeout
, чтобы избежать вечной блокировки. Давайте расширим предыдущий пример, чтобы продемонстрировать, как это работает. Во-первых, давайте добавим переменную channel3
, аналогичную двум другим, которые у нас уже есть. Далее давайте создадим еще одно волокно, которое отправит значение в наш третий канал. Например, взгляните на следующее:spawn do
puts "Starting fiber 3"
channel3.send 3
end
Наконец, мы можем переместить наше ключевое слово select
loop do
select
when v = channel1.receive
puts "Received #{v} from channel1"