Skip to content
uupaa edited this page Feb 2, 2017 · 20 revisions

このエントリでは Thread.js のユースケースについて、例を交えながら説明していきます。

基本的な使い方

メインスレッドからワーカースレッドにメッセージ "HELLO" を渡し、ワーカースレッドで "HELLO WORLD" に加工したものを送り返すシンプルな例です。

以下は、メインスレッドのコードです。
ワーカースレッドにメッセージを送り、結果を表示します。

<script src="./lib/WebModule.js"></script>
<script src="./lib/Thread.js"></script>
<script>

var thread = new WebModule.Thread("worker.js");

thread.post(["HELLO"], null, function(args) { // ワーカースレッドに "HELLO" を送る
    console.log(args[0]); // -> "HELLO WORLD";
});

</script>

Thread#post の args には 数値、文字列 や Object, Array といった基本的な型の他に、 このエントリに書かれている 型を使用できます。

こちらは、ワーカースレッドのコードです。
メッセージを受けて加工し、メインスレッドに送り返します。

/// worker.js
importScripts("./lib/WebModule.js");
importScripts("./lib/ThreadProxy.js");

var proxy = new WebModule.ThreadProxy(function(args, event) {
        // "HELLO" を受け取り "HELLO WORLD" に加工してポストバックします
        event.postback(args[0] + " WORLD");
    });

メインスレッドとワーカースレッド

この例では、メインスレッド上の TypedArray( Uint8Array ) の配列をワーカースレッドに譲渡しています。

Transferable Object の機能を使うために、Thread#post の第2引数に [ArrayBuffer] を指定しています。

var thread = new WebModule.Thread("worker.js");

var u8 = new Uint8Array(1024);

thread.post([u8.buffer], [u8.buffer]);
/// worker.js
var proxy = new WebModule.ThreadProxy(function(args, event) {
        var u8 = new Uint8Array(args[0]); // ArrayBuffer が渡ってくるので Uint8Array に戻します
        //
    });

WorkerThread to MainThread

以下の例は、上記の MainThread to WorkerThread とは反対に、ワーカースレッドのメモリをメインスレッドに譲渡する例です。

ワーカースレッドにURL( http://example.com/a.png )を渡し、 ワーカースレッド上で読み込みとデコードを行い、 デコードした結果をメインスレッドに返しています。

var thread = new WebModule.Thread("worker.js", function(args) {
    });

thread.post(["http://example.com/a.png"], null, function(args) {
    var u8 = new Uint8Array(args[0]); // ArrayBuffer が渡ってくるので Uint8Array に戻します
    //
});
/// worker.js
var proxy = new WebModule.ThreadProxy(function(args, event) {
        var url = args[0];

        var xhr = new XMLHttpRequest();
        xhr.responseType = "arraybuffer";
        xhr.onload = function() {
            loaded(xhr.response, url);
        };
        xhr.open("GET", url);
        xhr.send();

        function loaded(arrayBuffer, url) {
            var png = WebModule.PNG.decode(arrayBuffer);

            event.postback([png.buffer], [png.buffer]);
        }
    });

Thread Close

Thread.js では、ワーカースレッドをメインスレッド側から終了させる事が可能です。
また、ワーカースレッドの応答が無くなった場合に備えて、強制終了させる仕組みも備えています。

Close Thread by MainThread

以下は、ワーカースレッドをメインスレッド側からクローズする例です。

new Thread(, postMessageHandler, closeMessageHandler) のように、インスタンス作成時に closeMessageHandler を用意することで、 メインスレッド側ではワーカースレッドの終了状態(exitCode)を取得し、 ワーカースレッド側ではメインスレッドからの終了要求へ応答(YES, NO)を返すことが可能になります。

var thread = new WebModule.Thread("worker.js", function(args) {
        //
    }, function closeMessageHandler(exitCode) {
        switch (exitCode) {
        case Thread.EXIT_OK:      // 正常終了しました
        case Thread.EXIT_ERROR:   // ワーカースレッドでエラーが発生したため終了しました
        case Thread.EXIT_FORCE:   // ワーカースレッドを強制終了しました
        case Thread.EXIT_TIMEOUT: // ワーカースレッドの終了処理が応答を返さなかったため強制終了しました
        }
    });

thread.close();

こちらはワーカースレッド側のコードです。closeRequestHandler の中で、メインスレッドからの要求に yes() or no() で応答できます。

/// worker.js
var proxy = new WebModule.ThreadProxy(function postMessageHandler(args, event) {
        //
    }, function closeRequestHandler(yes, no) {
        if (1) {
            yes(); // 必要な終了処理を行い、安全に終了できる場合は yes を実行します。
        } else {
            no(); // 終了要求を拒否する場合は no を実行します。
        }
    });

Close Thread by WorkerThread

以下は、ワーカースレッドが自分自身を終了させる例です。

var thread = new WebModule.Thread("worker.js", null, function(exitCode) {
        switch (exitCode) {
        case WebModule.Thread.EXIT_OK:      // 正常終了しました
        case WebModule.Thread.EXIT_ERROR:   // ワーカースレッドでエラーが発生したため終了しました
        case WebModule.Thread.EXIT_FORCE:   // ワーカースレッドを強制終了しました
        case WebModule.Thread.EXIT_TIMEOUT: // ワーカースレッドの終了処理が応答を返さなかったため強制終了しました
        }
    });

thread.post();

ワーカースレッドで ThreadProxy#close を呼ぶことで自分自身を終了させます。

/// worker.js

var proxy = new WebModule.ThreadProxy(function(args, event) {
        proxy.close();
    });

Close Thread by Timeout

メインスレッドからの終了要求に対し、ワーカースレッドが適切に応答を返せない状況もあるかもしれません。
そのような状況に備えるために、Thread#close にはタイムアウト時間を設定できます。

var thread = new WebModule.Thread("worker.js", function postMessageHandler(args, event) {
    }, function closeMessageHandler(exitCode) {
        switch (exitCode) {
        case WebModule.Thread.EXIT_OK:      // 正常終了しました
        case WebModule.Thread.EXIT_ERROR:   // ワーカースレッドでエラーが発生したため終了しました
        case WebModule.Thread.EXIT_FORCE:   // ワーカースレッドを強制終了しました
        case WebModule.Thread.EXIT_TIMEOUT: // ワーカースレッドの終了処理が応答を返さなかったため強制終了しました
        }
    });

thread.close(1000); // ワーカースレッドから適切な応答(yes or no)がない場合は、1秒後に強制終了させます。
                    // この場合の exitCode は EXIT_TIMEOUT になります。

こちらはワーカースレッド側のコードです。handleClose の中で、メインスレッドからの要求に適切に応答していないため、 このままではタイムアウトしてしまいます。

/// worker.js

var proxy = new WebModule.ThreadProxy(function(args, event) {
    }, function(yes, no) {
        // 必要な終了処理を行い、安全に終了できる場合は yes() を実行します。
        // yes();

        // 終了要求を拒否する場合は no() を実行します。
        // no();
    });

なおメインスレッドで、Thread#close(-1) とした場合は、即座に強制終了させます。
この場合の exitCode は EXIT_FORCE になります。

MessagePack

MessagePack と組み合わせて使用する例です。

    var thread = new Thread("worker.js");

    var packed = WebModule.MessagePack.encode({
            msg:    "HELLO",
            date1:  new Date(),
            date2:  null,
            data:   new Uint8Array(10)
        });

    thread.post([packed.buffer], [packed.buffer], function(args, event) {
        var result = WebModule.MessagePack.decode(new Uint8Array(args[0]));
        console.log(result.msg);   // -> "HELLO WORLD";
        console.log(result.date1);
        console.log(result.date2);
        console.log(result.data);
        // done
    });
/// worker.js

importScripts("../node_modules/uupaa.messagepack.js/lib/MessagePack.js");
importScripts("../lib/ThreadProxy.js");

var proxy = new WebModule.ThreadProxy(function(args, event) {
        var obj = WebModule.MessagePack.decode(new Uint8Array(args[0]));

        obj.msg += " WORLD";
        obj.date2 = new Date();
        obj.data.set([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);

        var packed = WebModule.MessagePack.encode(obj);

        proxy.post([packed.buffer], [packed.buffer]);
    }, function(yes, no) {
        yes();
    });

Clone this wiki locally