Вчера мы видели, как создаются параллельные потоки с помощью слова start. Сегодня мы чуть подробнее остановимся на этом.
Вызов start создает промис, который выполняет блок кода параллельно основному потоку. После создания промиса управление тут же передается в основную программу, поэтому необходимо дождаться завершения работы треда.
my $promise = start { sleep 2; say 'Done'; } say 'Waiting...'; await $promise;
Эта программа создает промис $promise и ждет его выполнения. На печати появляется следующее:
$ perl6 start-1.pl Waiting... Done
Если убрать строку с await, то программа завершится прежде чем завершит работу блок кода из промиса, поэтому Done напечатано не будет.
Разумеется, возможно создать более одного промиса, и все они будут выполняться параллельно.
my @promises; for 1..10 -> $n { push @promises, start { say "Done $n"; } } say 'Waiting...'; await @promises;
Теперь создано десять потоков, и все они начинают работать сразу после создания. Поскольку на этот раз блок кода не содержит sleep, вывод программы может отличаться от запуска к запуску, например:
$ perl6 start-2.pl Done 1 Done 2 Done 3 Done 4 Done 5 Done 6 Waiting... Done 7 Done 8 Done 9 Done 10
Вместо того, чтобы сохранять промисы в массиве (это нужно, чтобы было что передать await), удобно воспользоваться вот таким приемом с gather и take:
await gather for 1..10 -> $n { take start { say "Done $n"; } } say 'Waiting...';
Еще проще конструкция с do:
await do for 1..10 -> $n { start { say "Done $n"; } } say 'Waiting...';
Синтаксически, слово start — это префиксный оператор, который делает то же самое что и вызов одноименного метода класса Promise. Первую программу можно было бы переписать так:
my $promise = Promise.start({ sleep 2; say 'Done'; }); say 'Waiting...'; await $promise;
Перед тем, как попрощаться сегодня, маленькое замечание: метод start применяется еще в нескольких ситуациях: при создании тредов, сапплаев и при запуске внешнего процесса. Поговорим обо всем этом в следующий раз.