Как инициализировать дочерний процесс с переданными функциями в Node.js - PullRequest
0 голосов
/ 23 мая 2018

Контекст

Я строю игру общего назначения , играя в ИИ-фреймворк / библиотеку , в которой используется алгоритм Поиск по дереву Монте-Карло .Идея довольно проста, фреймворк предоставляет каркас алгоритма, четыре основных шага: выбор, расширение, моделирование и обратное распространение.Все, что нужно сделать пользователю, это подключить четыре простые (иш) игровые функции его создания:

  1. функция, которая принимает игровое состояние и возвращает все возможные разрешенные ходы для игры
  2. функция, которая принимает игровое состояние и действие и возвращает новое игровое состояние после применения действия
  3. функция, которая принимает игровое состояние и определяет, закончилась ли игра, и возвращает логическое значение и
  4. функция, принимающая состояние и идентификатор игрока и возвращающая значение в зависимости от того, выиграл ли игрок, проиграл или игра вничью.При этом у алгоритма есть все, что ему нужно для запуска и выбора хода.

Что я хотел бы сделать

Я хотел бы использовать параллельное программирование дляувеличить силу алгоритма и сократить время, необходимое для запуска каждого игрового хода.Проблема, с которой я сталкиваюсь, заключается в том, что при использовании дочерних процессов в NodeJS вы не можете передавать функции дочернему процессу, и моя структура полностью основана на использовании функций, передаваемых пользователем.

Возможное решение

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

Я думал об одном способе сделать это, но он кажется настолько не элегантным, возможно, не самым безопасным, что я нахожусь в поиске других решений.Я мог бы, когда пользователь инициализирует фреймворк и передает ему свои четыре функции, получить скрипт для записи этих функций в новый файл js (назовем его my-funcs.js), который будет выглядеть примерно так:

const func1 = {... function implementation...}
const func2 = {... function implementation...}
const func3 = {... function implementation...}
const func4 = {... function implementation...}

module.exports = {func1, func2, func3, func4}

Затем, в файле дочернего рабочего процесса, я думаю, мне нужно будет найти способ для ленивой загрузки, требующий my-funcs.js.Или, может быть, я не стал бы, я думаю, это зависит от того, как и когда Node.js загружает рабочий файл в память.Все это кажется очень запутанным.

Можете ли вы описать другие способы получить желаемый результат?

Ответы [ 2 ]

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

child_process меньше о запуске пользовательской функции и больше о запуске нового потока для выполнения файла или процесса.

Узел по своей сути является однопоточной системой, поэтому для вещей, связанных с вводом / выводом, Node Event Loop действительно хорош в переключении между запросами, делая каждый немного дальше.См. https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

Похоже, вы пытаетесь заставить JavaScript запускать несколько потоков одновременно.Короткий ответ: не могу ... а точнее это действительно сложно.Посмотрите возможно ли достичь многопоточности в nodejs?

Так как же мы это сделаем?Вы на правильном пути: child_process.fork().Но для этого нужна жестко закодированная функция.Итак, как нам получить сгенерированный пользователем код на месте?

Я представляю хранилище данных, где вы можете взять userFn.ToString() и сохранить его в очереди.Затем разветвите процесс и дайте ему поднять следующую необработанную вещь в очереди, отмечая, что это так.Затем записывает результаты в другую очередь, и этот поток «GUI» затем опрашивает эту очередь, возвращая вычисленные результаты обратно пользователю.На данный момент у вас есть многопоточность ... и условия гонки.

Другая идея: создать службу REST, которая принимает содержимое userFn.ToString() и exec s.Затем в этом модуле вы вызываете другой «поток» (службу), ожидаете результаты и возвращаете их.

Безопасность: Да, мы просто выбросили это в окно.Независимо от того, выполняете ли вы пользовательскую функцию напрямую, вызываете child_process#fork или выполняете ее через службу, вы доверяете ненадежному коду.К сожалению, действительно нет никакого способа обойти это.

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

Предполагая, что безопасность не является проблемой, вы можете сделать что-то вроде этого.

// Client side
<input class="func1"> // For example user inputs '(gamestate)=>{return 1}'
<input class="func2">
<input class="func3">
<input class="func4">

<script>
  socket.on('syntax_error',function(err){alert(err)});
  submit_funcs_strs(){
    // Get function strings from user input and then put into array
    socket.emit('functions',[document.getElementById('func1').value,document.getElementById('func2').value,...
  }
</script>

// Server side

// Socket listener is async
socket.on('functions',(funcs_strs)=>{
  let funcs = []
  for (let i = 0; i < funcs_str.length;i++){
    try {
        funcs.push(eval(funcs_strs)); 
    } catch (e) {
        if (e instanceof SyntaxError) {
            socket.emit('syntax_error',e.message);
            return;
        }
    } 

  }
  // Run algorithm here
}
...