perl6 "Операция, ожидаемая первым" - PullRequest
0 голосов
/ 29 мая 2018

Эта программа создает поток для чтения каталога с помощью dir () и размещения файлов на канале.Рабочие потоки $ N читают этот канал и "обрабатывают" (печатают) файлы.

Однако я получаю сообщение об ошибке "Операция сначала ожидалась:".

Я прочитал ловушкистраницу об этой ошибке несколько раз, но до сих пор не могу понять ее.Кто-нибудь может объяснить, что здесь происходит?

Содержимое каталога:

$ ls
a  b  c  traverse-dir0.p6

Запуск программы:

$ ./traverse-dir0.p6 
traverse-dir0.p6
a
b
c
An operation first awaited:
  in sub MAIN at ./traverse-dir0.p6 line 24
  in block  at ./traverse-dir0.p6 line 5

Died with the exception:
    Cannot find method 'path': no method cache and no .^find_method
      in block  at ./traverse-dir0.p6 line 16

Программа traverse-dir0.p6:

#!/usr/bin/env perl6
# There is a thread to populate $dir-channel by reading filenames in a directory with dir()
# and $N worker threads to read the filenames from the $dir-channel.

sub MAIN( Str $dir = ".", Int :$N = 4 ) {

    my $dir-channel = Channel.new();
    my $dir-read = start {
        $dir-channel.send( $_ ) for dir $dir;
        $dir-channel.close;
    }

    my @workers = (^$N).map: {
        start {
            while my $file = $dir-channel.receive() {
                say $file.path;
            }
            CATCH {
                when X::Channel::ReceiveOnClosed { .resume }
            }
        }
    }

    await $dir-read, @workers;
}

1 Ответ

0 голосов
/ 29 мая 2018

Сначала о выводе исключения, выданного из await.При сбое асинхронной операции есть два интересных момента:

  • Где в программе мы хотели получить результат операции
  • Где в программе возникла проблема, которая означала операциюне может быть выполнена

Первая часть информации указывает местоположение await, и трассировка стека относится к этому.Вторая часть о том, почему исключение было повторно выброшено await, и указывает на проблему, которую необходимо исправить.

Проблема в этом случае состоит в том, что метод path вызывается дляобъект, у которого его нет.Это благодаря .resume, что не имеет смысла.Исключение указывает, что невозможно получить значение из канала.Его возобновление означает, что тело цикла выполняется с неопределенным значением в $file, в котором отсутствует метод path, что приводит к ошибке.(В качестве отступления: очень, очень редко .resume является правильным ответом.)

Наименьшее исправление в коде - заменить .resume на last, что завершает итерацию, когдаканал закрыт:

my @workers = (^$N).map: {
    start {
        while my $file = $dir-channel.receive() {
            say $file.path;
            CATCH {
                when X::Channel::ReceiveOnClosed { last }
            }
        }
    }
}

Тем не менее, гораздо проще преобразовать Channel в итеративный Seq.Это автоматически обрабатывает завершение итерации, когда Channel закрывается, поэтому нет возни с исключениями:

my @workers = (^$N).map: {
    start {
        for $dir-channel.Seq -> $file {
            say $file.path;
        }
    }
}

А так как start является префиксом оператора, который в дальнейшем сокращается до:

my @workers = (^$N).map: {
    start for $dir-channel.Seq -> $file {
        say $file.path;
    }   
}   

Я понимаю, что это была, вероятно, упрощенная версия более интересной проблемы, или, возможно, она была сделана для изучения различных концепций параллелизма Perl 6, но весь лот можно было бы заменить на:

sub MAIN( Str $dir = ".", Int :$N = 4 ) {
    race for dir($dir).race(batch => 1, degree => $N) -> $file {
        say $file.path;
    }
}

, который имеетта же семантика, но экономит запуск и управление работниками, но при этом контролирует количество работников и обеспечивает одинаковое распределение файлов среди работников.

...