Курс языка программирования Raku / Функциональное, конкурентное, реактивное и веб-программирование / Конкурентное программирование / Каналы

Чтение после конца

Что произойдёт, если вызвать .receive больше раз, чем есть значений? Ответ зависит от того, закрыт ли канал.

Возьмём прежний пример с производителем и потребителем: фоновая задача отправляет три значения и закрывает канал. А что, если потребитель попросит четвёртое?

my $c = Channel.new;
start {
    $c.send($_) for 1..3;
    $c.close;
}

say $c.receive; # 1
say $c.receive; # 2
say $c.receive; # 3
say $c.receive; # одним получением больше, чем нужно

На закрытом канале лишнее получение бросает исключение

Закрытие не выбрасывает уже стоящие в очереди значения — три вызова .receive выше по-прежнему вернут 1, 2 и 3. Но как только канал закрыт и пуст, новых значений появиться уже не может, и, вместо того чтобы позволить вам ждать вечно, Raku возбуждает исключение:

Cannot receive a message on a closed channel

Это исключение X::Channel::ReceiveOnClosed. Оно скорее полезно, чем досадно: оно недвусмысленно сообщает потребителю, что канал вычерпан. На практике до него не доходит, если потреблять через .list (который аккуратно останавливается на закрытии) или крутить цикл лишь пока значения остаются, — оба способа показаны на предыдущей странице.

На открытом канале лишнее получение блокирует выполнение

Если производитель канал так и не закрыл, .receive не может знать, придёт ли ещё значение, и попросту ждёт:

my $c = Channel.new;
$c.send(1);
say $c.receive; # 1
say $c.receive; # здесь ждёт — канал открыт, но пуст

Программа зависает на втором .receive, потому что значение может ещё быть отправлено. Об этой опасности и предупреждала предыдущая страница: забудьте .close — и обычное «дождись следующего значения» превратится в ожидание, которое никогда не кончится.

Итак, два исхода противоположны, и это полезно. Закрытый канал, опустев, отказывает сразу — вы получаете видимое исключение, которое можно перехватить. Открытый канал продолжает ждать — что совершенно правильно, пока производитель ещё работает, но оборачивается безмолвным зависанием, если производитель тихо остановился, не закрыв канал. Закрытие канала как раз и превращает это безмолвное зависание в ясный сигнал о том, что поток закончился.

Практика

Пройдите 1 тест по содержанию этой темы.

Навигация по курсу

Тест — Закрытие канала   |   Тест — Чтение после конца


💪 Или перейдите сразу к упражнениям этого раздела.