Встроенный:: Java конфликтует с Parallel::ForkManager

#perl #parallel-processing #inline

#perl #параллельная обработка #встроенный

Вопрос:

У меня возникли проблемы с одновременным вызовом обоих Parallel::ForkManager и Inline::Java одновременно. В частности, если я вызываю Inline::Java с JNI => 1 опцией (которая у меня есть), то процесс fork не возвращается к родительскому. Вот коды:

 use Parallel::ForkManager;

##### Calling Inline::Java #####
use Inline Java => <<END, JNI => 1;

END
###### End of Inline::Java #####

my $pm = Parallel::ForkManager->new(2);
for my $i (0..1) {
    $pm->start and next;
    print "Inside process $in";
    $pm->finish;
}
$pm->wait_all_children;
print "Back to Parent.n";
  

Если я запускаю эту программу, она переходит в дочерние процессы, но никогда не возвращается к родительскому. Если я удалю 3 строки между комментариями, все будет работать нормально. Если я изменю JNI => 1 на JNI => 0 (не то чтобы мне разрешалось изменять этот параметр для моих целей), то появится сообщение об ошибке Lost connection with Java virtual machine at /usr/lib64/perl5/site_perl/5.8.8/x86_64-linux-thread-multi/Inline/Java.pm line 975 .

Кто-нибудь знает, как разрешить конфликт? Я также должен вызвать Inline::Java перед параллельным процессом, поэтому использование require после завершения параллели не является вариантом. Спасибо!

Ответ №1:

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

Вам нужно отложить подключение к виртуальной машине, чтобы это было сделано в дочерних системах, а не в родительской.

Вы могли бы переместить все, что связано с Inline ::Java, в другой модуль, а затем использовать require Child; (не use Child; ) после start .

Если вам нужно использовать Inline::Java перед запуском дочернего элемента, сделайте это в другом процессе.

Комментарии:

1. Ценю ваш анализ. Однако ваше решение, к сожалению, не подходит для меня для реализации, поскольку я отметил в конце исходного вопроса, что процесс Inline:: Java должен выполняться до разветвления. кстати, Inline::Java уже находится в отдельном модуле. Я просто объединил все скрипты здесь для иллюстрации.

2. @Zhang18, прочитай еще раз. Мое решение не мешает вам использовать Inline::Java перед циклом P::FM. Вам просто нужно сделать это в отдельном процессе.

3. Я вижу — вы абсолютно правы. Вместо того, чтобы обращаться к каждому дочернему процессу для вызова JVM, мне удалось просто перейти к одному дочернему процессу, вызвать JVM, вернуть результаты родительскому, а затем разветвить на множество дочерних процессов. Итак, ключ в том, чтобы никогда не запускать JVM в родительском. Спасибо большое!

Ответ №2:

Использование forks с Inline::Java будет проблемой. Ваш сценарий perl должен поддерживать TCP-соединение с JVM. Когда вы разветвляете новый процесс, дочернему процессу передаются те же файловые дескрипторы для связи с JVM, поэтому родительский и все дочерние процессы используют одни и те же сокеты. Это не сработает. Вам необходимо перепроектировать свое приложение.

Одна из возможностей (которую вы уже исключили) заключается в том, чтобы отложить запуск JVM до завершения fork, запустив новую JVM в каждом дочернем процессе.

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

 my $java = ... entry point to JVM ...

for my $n (1 .. $num_threads) {
    $java->startNewThread(@inputs)
}
$java->waitForThreadsToFinish();
$result = $java->getResults();
  

Perl также имеет свою собственную модель потоков (см. threads и threads::shared ). Я сомневаюсь, что потоки Perl будут работать для решения этой проблемы, но, возможно, стоит попробовать.


Обновление: другая возможность, которая упоминается в Inline::Java документах, заключается в использовании общей виртуальной машины. Вызывайте Inline::Java с опцией SHARED_JVM => 1 , и когда запускается новый дочерний процесс, вызывайте Inline::Java::reconnect_JVM() из дочернего процесса, чтобы установить новое соединение. Недостатками этого подхода являются

  1. это сохраняет JVM активной после завершения программы, поэтому вы должны помнить, что нужно отключить JVM
  2. это несовместимо с опцией JNI => 1 , которая может привести к нарушению работы операционной системы.

Комментарии:

1. Потоки Perl не помогут, потому что у вас все равно будет та же проблема с несколькими задачами, пытающимися одновременно использовать один и тот же сокет.

2. Просто для ясности, подход Java thread должен работать. Мой комментарий касается только замечаний в последнем абзаце mob.