2020/06/28

[js]マルチスレッドしたい

JavaScriptはシングルスレッドだったよな、と思っていたのだが、Node.jsではworker_threadsなるものが使えるそうだ。
バックグラウンドで結果を待ち続けつつも表での処理は続けたかったので、2つjsファイルを起動しないといけないのか悩んでいたのだった。


Worker Threads | Node.js v12.18.1 Documentation
https://nodejs.org/docs/latest-v12.x/api/worker_threads.html

私が見たときはNode v14.4.0だったのだが、動かしているのはNode v12.18.1だ。

 

とりあえず、一番上にあったコードを動かしてみよう。
・・・何も起きない。。。
__filenameは実ファイルに置き換えないといけないのかと思ったが、そういうわけでもない。
ログを埋め込んで、isMainThreadで分岐してmain threadと認識しているところまでは分かった。

 

そういえば、module.exportsは外部に提供する場合だったような。。。
今さらだが、module.exports に関数を代入する書き方をした場合、それをrequire()すると関数本体だけが渡されたことになる。
だから、require()した変数に()を付けるだけで実行できる。

 

などと勉強しながらやっていったのだが、たぶんこの例は単独で動かすことはできない。
だって、mainじゃない方の分岐ではrequire()でパースするライブラリを必要としているし。

よし、このサンプルは忘れよう。


次はここだ。

worker_threadsを使ったNode.js マルチスレッドプログラミング - kakts-log
https://kakts-tec.hatenablog.com/entry/2018/12/14/005316

いきなりAPI仕様を見ても分からんので、使い方をある程度見てからにする。
worker.workerDataのサンプルが動いた。

  1. new Worker()でworker thread生成。__filenameは__FILE__みたいなものか。workDataでworkerに値を与えられる。
  2. 起動したworker threadはコンソールログを出してmain threadにスレッドIDをpostMessageする
  3. main threadではEmit Eventで”message”を待ち受けていて、postされたメッセージをコンソールログに出す

main threadでもworkerにpostMessageしているが、これは使っていないようだ。

 

workerDataに関数は指定できるのだろうか?

const {Worker, isMainThread, workerData, parentPort, threadId} = require('worker_threads');

function yoshio(name) {
  console.log(`yoshio=${name}`);
}

if (isMainThread) {
  // mainスレッド
  console.log(`Main Thread!!! isMainThread: ${isMainThread}, threadId: ${threadId}`);

  // 4つ作成する
  for (let i = 0; i < 4; i++) {
    const worker = new Worker(__filename, { workerData: yoshio });
    worker.on('message', (message) => {
      console.log(`[main] message got from worker : ${message}`);
    });

    worker.postMessage("from tokyo")

  }
} else {
  // workerスレッド
  console.log(`[worker] workerData: ${workerData} isMainThread: ${isMainThread}`)
  parentPort.postMessage(`ThreadId: ${threadId}`)
}

ダメだった。

$ node main.js
Main Thread!!! isMainThread: true, threadId: 0
internal/worker.js:194
    this[kPort].postMessage({
                 ^

DOMException [DataCloneError]: function yoshio(name) {
  console.log(`yoshio=${name}`);
} could not be cloned.
    at new Worker (internal/worker.js:194:17)
    ........


classにしたら渡せるのかと思ったのだが、エラーにはならないものの、プロパティしか渡されなかった。JSON.stringify()で見たところ値まで一緒に出てきた、し関数も出てこないし、classが渡されたというよりはコピーしてプロパティだけが残された、ということか。

 

うーん、pthreadみたいにvoid*で大元から渡せると楽なのだけど、同期とか考えると参照渡しはできないということか。。。

試しに、new Worker()の前にインスタンスを作って、スレッド分岐後のmain thread側で書き換えてみたのだが、反対側のthreadには反映されなかった。
アドレス値が表示できればもう少し自信が持てるのだが、まだJavaScript慣れしていないので実装がうまくないだけかもしれん。。。

 

Node.jsのworker_threadsに何を渡すべきか - Qiita
https://qiita.com/ayatty/items/1f5dde995251ac5800ad

共有メモリ方式なのかな?

ただ、私がやりたかったのはメソッドを含めたオブジェクトの共有なのだ。
そういうことは、おそらくできないような気がする。

オブジェクトは1つのスレッドにまとめて、その結果だけをコピーできる形でもらうような設計にする方が無難な気がしてきた。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。