Синхронизация / планирование потоков в Perl - PullRequest
1 голос
/ 12 августа 2010

У меня есть объект perl с несколькими функциями.Каждая функция вызывается один раз из основной программы.Я хотел бы запустить некоторые функции параллельно, чтобы сэкономить время.Я не могу запустить их все вместе, так как некоторые функции зависят от результатов предыдущих функций.

Я думал о чем-то вроде этого:

  1. Для каждой функции сохраняйте флаг, которыйинициализируется значением false и устанавливается в значение true функцией, когда она заканчивается (например, последняя строка в func1 будет $is_func1_done = 1).
  2. Запуск каждой функции с циклом, который ожидает, пока всефлаги функций, от которых это зависит, истинны.Например: если func1 зависит от func2 и func3, тогда:

     sub func1 {
      while (!($is_func2_done && $is_func3_done)) {
       # do nothing
      } 
      # do work
     }
    

Тогда я могу сразу запустить поток для каждой функции, но фактическую работу каждойФункция запустится только тогда, когда она будет готова.Имеет ли это смысл?Нужны ли здесь какие-нибудь замки на флагах?Распространены ли такие циклы while?- приходит на ум термин занятое ожидание ... может быть, большая часть моего процессорного времени будет потрачена на эти while с?Есть ли более стандартное решение для этого?

Ответы [ 2 ]

2 голосов
/ 12 августа 2010

Имеет ли это смысл?

Да - каждая задача знает свои предварительные условия и ожидает их выполнения перед выполнением.Это один из множества допустимых дизайнов, хотя вам может быть сложно масштабировать его по мере увеличения количества задач и усложнения их взаимозависимостей.

Нужны ли здесь какие-либо блокировки на флажках?

Да.Флаги должны быть shared , чтобы один поток мог ими манипулировать, а другой видел их, а для безопасного использования общие переменные должны быть lock() ed.

IsИспользование таких циклов в то время как общие?- в голову приходит термин «ожидание занято»

К сожалению, да, но Не делайте этого, пожалуйста. Общие переменные в perl могут служить условными переменными через которые потоки могут отправлять уведомления друг другу:

sub func1 {
    {
        lock(%shared_state);
        until ($shared_state{ "func2 done" } and $shared_state{ "func3 done" }) {
            cond_wait(%shared_state);
        }
    }
    # do work -- note that %shared_state is unlocked

    # now tell others that we're done
    lock(%shared_state);
    $shared_state{ "func1 done" } = 1;
    cond_broadcast(%shared_state);
    # %shared_state will be unlocked, and broadcast delivered when we leave this scope
}

Когда вы cond_wait, переменная общего доступа разблокируется, и ваш поток переходит в спящий режим.Нет необходимости в занятом цикле.

Есть ли более стандартное решение для этого?

$thr->join, поскольку Синан предлагает , это простои естественный способ дождаться окончания работы определенного потока. Thread :: Semaphore может выполнять аналогичную, но более сложную функцию (и, что полезно, может быть инициализирован до значений меньше нуля).Обычная необходимость «дождаться окончания этих 5 потоков» может быть достигнута с помощью Thread :: Barrier .TMTOWTDI.

2 голосов
/ 12 августа 2010

Вы должны использовать $thr->join, чтобы дождаться окончания потока.

Например:

#!/usr/bin/perl

use strict; use warnings;
use threads;

my @threads = map threads->create($_), qw( func1 func2 );
$_->join for @threads;

my $thr3 = threads->create('func3');
$thr3->join;

sub func1 {
    for (1 .. 5) {
        print "func1\n";
        sleep 1 + rand 3;
    }
    return;
}

sub func2 {
    for (1 .. 5) {
        print "func2\n";
        sleep 1 + rand 2;
    }
    return;
}

sub func3 {
    print "Time to do some work\n";
}

Я не знаю, является ли обычным использование таких while циклов: я бы не стал.

...