この前のナンバーリンクソルバーについて改良を行なっていたのですが、さすがにSATソルバーをフロントエンドでそのまま回すのは負担が重いので、Web Workerで処理を行ってみることにしましたが、Emscripten特有の注意点が1つありました。
おことわり
なお、この記事はEmscriptenでコンパイルした後にWeb Workersで複数スレッド動作をさせることを前提としたC/C++コードを書く、という意味合いの記事ではありません。もともとあったC/C++のコードベースをEmscripten化したものについて、Web Workerで動作させる、という内容です。
Web Workerとは
JavaScriptは基本的にシングルスレッドのモデルをとっているので、一度には1つの処理しか進行しません。AjaxやsetTimeout
、イベントなど非同期実行の処理は数多くありますが、これらとて1つの処理をこなしている間に別なことをすることはできません。
一方、Web Workerは、ブラウザのメイン動作とは別個のスレッドでJavaScriptを実行できる機能です。ここで複雑な処理を進めれば、途中で処理を切らなくても、ブラウザのUIを固まらせたりすることはありません。
制約事項
ただし、もともとシングルスレッドだったところに「後付け」のような形で導入されたこともあって、できること・できないことがはっきりしています。
できること
- ブラウザと関係ない純粋JavaScriptとしての処理
- XMLHTTPRequest、Web Socketなどを使った通信
-
setTimeout
やsetInterval
といったタイマー系API -
importScripts
による別なスクリプトのロード -
console
を用いたデバッグ出力 -
postMessage
によるメインスレッドとのデータのやり取り
できないこと
- DOM操作
- メインスレッドの変数操作
とりわけ、メインスレッドとデータをやり取りする手段はpostMessage
だけです。
postMessage
とは
postMessage
は、ブラウザ内の異なるコンテキスト(クロスドメインなど)で安全にデータをやり取りするための仕組みです。Web Workerと本体スレッドの間でも、これを利用してデータをやり取りすることになります。
- データの送信…
postMessage()
- データの受信…
message
イベントとして来るので、onmessage
としてハンドラをセットする、もしくはaddEventListener
で待ち受ける
Emscriptenコードが…動かない
さて、そんな感じでWeb Workerを動かすのですが、最初からC/C++で書かれたプログラムやライブラリは、性質上DOMにアクセスすることもないので、Web Workerで動かすには最適です。
で、Worker内からimportScripts
でEmscriptenコードを呼び出して、実行しようとしたのですが、なんと「Module
が見つかりません」とエラーになってしまいました。コードを読んでみると、window
のある通常のブラウザ環境では自動でwindow.Module
を定義するのですが、Worker環境ではそうなっていなかったためにModule
が利用できない状態となっていました。対策として、事前にvar Module={};
としてセットすべき変数を用意しておくことで、正常に呼べるようになりました。
このあたりについての実際のコードがありますので、ご参考になれば幸いです。
外部リンク
- Web Workerを利用する - MDN