質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

3回答

1249閲覧

Node.js、node-ffiが処理をブロッキングする?

KohnoseLami

総合スコア17

DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2023/01/16 01:22

編集2023/01/16 01:32

前提

Node.jsでは難しい処理をGoで実装してもらい、それをDLLで呼び出せるようになっているのですがそれを呼び出すためにnode-ffiを使用しており非同期的に大量に処理を行いたいのですが何かPythonで非同期処理内にブロッキングされる同期処理を含めた時のようなプログラム全体が一時停止するような挙動が起こっています。
これを改善する方法はNode.jsにありますか?Pythonなどではプロセスやスレッドを分けることで解決することが出来ると思うのですが

実現したいこと

DLL内の関数を呼び出してる間も他の処理を続行させたい

発生している問題・エラーメッセージ

Goで書かれたDLL内の関数にはウェブリクエストを行うものがありそれらをPromise.allなどで大量に関数を呼び出し待機したいのですがaxiosなどの全てJavaScriptで書かれたコードよりも著しく低速かつ一斉に処理が完了するためDLL内の関数の処理が順番に行われている気がします。
DLLの処理がJavaScriptを止め、その処理が終わり次第JavaScriptの処理が始まり一斉に結果がconsole.logされることからそうではないかと思っております。
またほかにもDiscord.jsなどで他の処理を行いながら呼び出すとBotが停止することなどからブロッキングされていると考えました

該当のソースコード

全てのコードを載せることが難しいため問題であると思われる処理のみを参考程度に書き直し抜粋して記載します。

javascript

1import ffi from 'ffi-napi'; 2 3const library = ffi.Library('example.dll', { 4 request: ['string', ['string']] 5}); 6 7const a = async() => { 8 const r = library.request('nanika'); //ここでブロッキングされている(おそらく) 9 console.log(r); //上の処理が実行されている間はここにたどり着けない 10} 11 12new Array(1000).fill(0).forEach(() => a());

試したこと

そもそもほぼすべてがCallbackやAsync的な言語のためブロッキングすること自体を初めて知り記事などでもほぼ解説されていないため言語仕様的な物から説明していただけると助かります。
Pythonは同期がメインになっているだけで非同期が使用できる、全てが非同期に出来るわけではなくスレッドなどを活用する必要があるように、JavaScriptも非同期がメインになっているだけで同期処理も使用でき、同じように同期処理を実行している間は他の処理なども全て停止させるのかなど認識が甘いところがありました
今思えばfs.readFileSyncなども同期処理でただファイル読み込みには大した時間がかからないから気になっていないだけなのかなと思い、そういった場合だと大量にファイルを読み込んだ場合にはもちろんブロックする時間が長くなりパフォーマンスに多大な影響を与える場合があります。
こういった説明はあまりされておらずただトップレベルawaitが出来ない環境で順番に処理を実行するにはとても有効なものだと思っていました、ですがそうではないのでしょうか?

補足情報(FW/ツールのバージョンなど)

Node.js v18.12.1

追記

https://nodejs.org/ja/docs/guides/blocking-vs-non-blocking/
公式でこのようなブログがありました
認識は間違ってなさそうです、私が甘いようです

ですが、これならばSyncは完全にすべての処理を停止させますがasync/awaitの場合は確かに順番に処理をさせることが出来ますが他の処理は止まりません
この場合、async/awaitを同期化と呼ぶのはいかがなものなのでしょうか?
syncとasync/awaitでは意味が全然違いますが両方とも同期処理と呼ばれてしまっては混同してしまいます。
私はasync/awaitはあくまでも非同期処理で待機をすることが出来るものであって他の処理まで止めるものではないという認識がありました、それに対しSyncは完全にその処理中にそのスレッド、プロセスごと停止させるのだと思います。
これらを一括りにして同期化と呼ばれるとSyncはブロッキングしないものかと思ってしまっていました

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2023/01/16 01:33

> この場合、async/awaitを同期化と呼ぶのはいかがなものなのでしょうか? どこでそのような文言を見たのですか?
KohnoseLami

2023/01/16 01:40

Googleなどで「JavaScript 同期」などと調べると大量に出てきます
maisumakun

2023/01/16 01:47

そして、この質問が解決すべき課題は何なのでしょうか?
KohnoseLami

2023/01/16 01:51

すみません、後半少しずれてしまい 質問を書きながらある程度理解して行けたのですがあまりにもおかしくて徐々に本題の質問が曖昧になってしまいました 回答にも書かせていただきましたが、ffi-napiにて時間のかかる同期処理をブロッキングさせないようにしたいです。
guest

回答3

0

node-ffiに関しては詳しくないのでドキュメントを読んでみました。
https://github.com/node-ffi/node-ffi

確かに同期処理でブロッキングされているようです。
しかし、async実行なる操作がチュートリアルに記載されており、それを利用すれば非同期実行にできるのでは?
https://github.com/node-ffi/node-ffi/wiki/Node-FFI-Tutorial#async-library-calls

では早速実装していきたいところですが、
node-ffiの実装はPromiseですらないコールバック時代の遺産ですので、
JavaScript流儀の書き方知ってないと応用は難しそう

JavaScriptでは非同期処理の実現をコールバック→Promise→async/awaitという流れで改良していますが、
Promise流儀の書き方に変換しなければなりません。

従って、質問文をlibrary.request.async('nanika')に変更しただけでは不十分で
そこからコールバック→Promiseへの変換を行う必要があります。
コードで表現するとこんな感じになります。

js

1import ffi from 'ffi-napi'; 2 3const library = ffi.Library('example.dll', { 4 request: ['string', ['string']] 5}); 6 7// コールバック→Promiseへの変換はasync関数を定義するだけでは不十分で、 8// このように記述する必要がある 9const a = id => new Promise((resolve, reject) => 10 library.request.async(id, (err, res) => { 11 if (err) return reject(err); 12 resolve(res); 13 }); 14); 15 16// Promiseを同期処理っぽく扱うためにawaitを使いたいが、これはasync関数の中でしか使えないので宣言しつつ実行 17const main = async () { 18 // mapメソッドでPromiseインスタンスの配列を作る 19 const promises = new Array(1000).fill(0).map((_, i) => a(i)); 20 // Promise.allで全てのPromiseインスタンスの実行が完了するまで待つ 21 const results = await Promise.all(promises); 22 console.log(results); 23} 24main();

これで一旦動作を確認してみてください。

投稿2023/01/16 03:11

miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KohnoseLami

2023/01/16 10:52

丁寧に回答ありがとうございます! 頂いたコードとほぼ同じで const response: string | null = await new Promise((resolve, reject) => request.async(JSON.stringify(requestPayload), (error, response) => error ? reject(error) : resolve(response))); といった具合に実装させていただきました これにより断然前の状態より非同期に処理されていますがやはり少しまだ1000件などになってくるとブロッキングされているような感じがしますね、、 ffi-napiのasync処理を見てみないと分からないですがスレッドに情報渡したり何かしらでブロッキングが発生しているのですかね?
guest

0

ベストアンサー

node-ffi の公式のドキュメントが参考になるんじゃないでしょうか。

Async Library Calls

投稿2023/01/16 01:57

編集2023/01/16 01:57
int32_t

総合スコア20832

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2023/01/16 01:59

node-ffiとnode-ffi-napiは別物です。
KohnoseLami

2023/01/16 02:20 編集

ffi-napiのreadme上に「For a more detailed introduction, see the node-ffi tutorial page.」という感じで貼られているtutorial pageに誘導されていました https://github.com/node-ffi-napi/node-ffi-napi そのためすこしそのasyncメソッドが参考になるかもしれません 試してみます、ありがとうございます
KohnoseLami

2023/01/16 02:46

こちらのasyncメソッドで作成してみたところ望み通りの動作が確認できました callbackだったのでPromiseでラップしてあげていい感じに使うことが出来ました!
guest

0

Googleなどで「JavaScript 同期」などと調べると大量に出てきます

単に筆者がわかっていないだけの情報が引っかかっているだけです。

投稿2023/01/16 01:45

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KohnoseLami

2023/01/16 01:49

async/awaitを逐次処理等同期処理という言葉を用いらない方が確実に良いですよね 逐次処理もあまり合っている表現なのかはわかりませんが 前々からSEOが優れているプログラム解説してる記事はよろしくないと言われていますがこういう感じなんですね 少し後半はずれてしまいましたが前半のsync的な同期処理をブロッキングさせないためにはどうしたら良いのでしょうか? PythonなどではThreadなどがありますがNode.jsのスレッドは値の受け渡しなど色々な問題でよろしくないと聞きます
maisumakun

2023/01/16 01:55

ffi-napi自体が「It is recommended to avoid any multi-threading usage of this library if possible.」と書いてあるような状況で、「リクエストを含んだ処理」をGoのDLLで進める、というのがマッチしていない印象です。 https://github.com/node-ffi-napi/node-ffi-napi リクエストはNode側で行って、Goはその結果を処理させるだけにする、あるいはGoで実行ファイルを作って、Nodeからはプロセスとして呼び出すなど、処理の切り分けを変えて解決する必要があるのではないかという印象です。
KohnoseLami

2023/01/16 01:59

んー、なるほど、ちょうど避ける必要があるのですね それがリクエストをGoで行う必要がありこの体制を崩すのは少し難しいんですよね 一応threadを使えば大丈夫そうだけど公式的には避けるべきだしあまり推奨はされないけど道はそれしかないような状態でしょうか?
maisumakun

2023/01/16 02:01

逆に、GoサイドをDLLでなく実行ファイルにする(別プロセスで実行)、という選択肢はどうなのでしょうか?
KohnoseLami

2023/01/16 02:11

それもそれでGoサイドは少し抜けていたのですがほぼすべて私が書いたわけではなくGoのオープンソースの物をDLLにコンパイルしNode.jsに移植した物でありメインはPythonとJavaScriptしか書けないっていうのとDiscord Botなどでも使用したいため全てGoで構築するのは難しいというのとPythonなどでもこのDLLを呼び出して使用するためロードさえ済ませてしまえばほぼ同じ構文で重要な部分だけをGoに託せるというのが魅力に感じており私の現在の状況だとそういったDLLを使用しない等の方向性ではあまり進めないという感じになっています 簡単に見た感じだとあとはGo側がAsyncに対応されればJavaScript側でもAsync、Callbackとして呼び出せるぐらいですかね
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問