HDS#23 非同期処理

こんにちは、開発担当の杉原です。

 

気づいたらブログ記事のストックがなくなってましたね。うっかり先週の更新を忘れてしまいました。

暇があるときに書き溜めていたんですが、ストックの存在に甘んじていたら忙しくなってブログに手を付けられなかったというのもあります。

あー、誰かにブログ記事更新を丸投げして、自分は別業務の方に注力したいなー。

 

ってな感じのことをJScriptにて実現しようというのが今回の【How to Demo3D Scripting】です。

あ、先日製品名称変更の案内記事がありましたが、このコーナー名はそのままにしておきますね。

さて、待ちを伴うAという処理があるとします。

この処理を実行したいけど、待ってる間にBという処理もやりたい、そんなことありませんか?

 

ここでは例として、目的地まで移動して、移動完了後にメッセージ出力する処理をしながら、1秒ごとに自分の座標をメッセージ出力する処理を行うVisualを実装することにします。

雑ですが、上図のようなBoxに対して処理を実装していきましょう。

 

ではまず、OnInitializeイベント発生時に原点(0, 0, 0)まで移動して、移動完了後にメッセージを表示するようにします。

function Box1_OnInitialize( sender : Demo3D.Visuals.BoxVisual )
{
    sender.MoveTo(Vector3.Zero, 0.3);
        
    sender.WaitForAnimator();
        
    print("到着");
}
// JScript向けのため省略
// JScrpitタブを選択してください。

この状態で実行して、意図したとおりに動作することを確認します。

 

動画で見たほうがわかりやすいかと思いますので、動画で結果を用意しました。

MoveToの引数「0.3」は秒速0.3mを意味しているので、結構ゆっくり動きますね。

 

次はひとまず、この処理に続けて「1秒ごとに自分の座標をメッセージ出力する処理」を追加してみます。

function Box1_OnInitialize( sender : Demo3D.Visuals.BoxVisual )
{
    sender.MoveTo(Vector3.Zero, 0.3);
        
    sender.WaitForAnimator();
        
    print("到着");
        
    while(true) {
        print(sender.WorldLocation);
                
        wait(1);
    }
}
// JScript向けのため省略
// JScrpitタブを選択してください。

さて、これで実行するとどうなるのか。

とまあ、移動が終わってから座標の出力が始まってしまったわけです。

 

理由はWaitForMove()関数によってBoxの移動が終わるまで処理がブロックされ、次の処理に進めないためです。

この問題を解決する方法が今回の非同期処理となります。

 

そもそも同期とは何ぞやということで、図にすると以下のような感じになります。

細長い長方形が「処理が行われている時間」を表しています。

このように呼び出した処理が終わって、結果が返ってから次の処理へ進むという状態を同期がとれているものとします。

 

一方、非同期な処理は下図のようになります。

呼び出した処理の結果を待たず、次の処理へ進んでいます。それどころか、処理の終端まで来た場合はその関数自体が終了したものとして、ライフラインが消滅しています。

 

さて、こうした非同期な処理の方法は.NET Frameworkなんかでも実装されているのですが、Emulate3DのJScriptでは次のようにして非同期に処理を行うことができるのです。

function Box1_OnInitialize( sender : Demo3D.Visuals.BoxVisual )
{
    do {
        sender.MoveTo(Vector3.Zero, 0.3);
                
        sender.WaitForAnimator();
                
        print("到着");
    };
        
    while(true) {
        print(sender.WorldLocation);
                
        wait(1);
    }
}
// JScript向けのため省略
// JScrpitタブを選択してください。

do{ }の中にある処理が非同期に実行される処理になります。

 

早速これを実行してみます。

無事要件を満たすことができましたね。

 

ちなみに、先ほどのコードでは延々と「現在座標を表示する」処理のループが行われるため、OnInitializeイベント処理が終了することがありません。

別にOnInitializeイベントであればそれでもかまわないのですが、例えばOnRxBeforeTransferイベントなんかだと、イベント処理が終わらないと次のステップに進まないため、処理が終わらず無限ループし続けるのは問題となります。

 

そのような場合は以下のように現在地を表示する処理の方を非同期に実行するよう実装することで、移動が完了したらOnInitializeイベント処理を終わるようにすることができます。

function Box1_OnInitialize( sender : Demo3D.Visuals.BoxVisual )
{
    do {
        while(true) {
            print(sender.WorldLocation);
                        
            wait(1);
        }
    };
        
    sender.MoveTo(Vector3.Zero, 0.3);
        
    sender.WaitForAnimator();
        
    print("到着");
}
// JScript向けのため省略
// JScrpitタブを選択してください。

非同期処理は、実はそこまでよく使うものでもないと思っているのですが、なんだかんだで覚えておくと役に立つ場面が時折ありました。

ですので、この記事がいつか誰かの役に立つことを願っています。


 

さて次回の【How to Demo3D Scripting】は「子供を探す」です。

更新は11/22(木)を予定しておりますので、よろしくお願いします!