MikoScript 言語仕様

 スレッド

 スレッドとは、本言語では、スクリプトを独立して実行できる単位のことです。
スクリプトの実行時に、スレッドは、複数同時に存在し得ますが、同時には
1つのスレッドだけしか実行されません。しかし、各スレッドの実行は、適時
切り換われるようになっています。

 本言語のスレッドは、本インタプリタ自身で管理しています。
これは、OSが管理するプリエンプティブなスレッドではありません。
本システムでは、基本的に、ノンプリエンプティブなマルチスレッドになります。

 本言語のスレッドは、コルーチン、あるいは、ファイバー的な形態で
実現されています。このスレッドは、OSが管理するスレッドと比較すると、
オーバーヘッドが少なく、軽量で、切り換えも高速に行なわれます。また、
なによりも、簡易に使うことができます。しかし、各スレッドの実行は、
自動的には切り換わらないので、各スレッド自身で操作する必要があります。
これは、面倒な場合もありますが、自ら任意に制御できるというメリットも
あります。

●スレッドの状態
 このような動作形態になっている本言語の各スレッドは、その生存中、次の
状態のうちのどれかになります。

実行中:
 現在スクリプトを実行しているスレッドが、この状態になります。
 この状態には、1つのスレッドだけしかなれません。
 この状態になるスレッドがない時もあります(全スレッドが睡眠時)。
 この状態のスレッドは、「スクリプトの実行権」を保持しています。
 この実行権は、「実行中」のスレッド自身が操作しない限り、
 他のスレッドに自動的に移譲されることはありません。
 これが、ノンプリエンプティブなマルチスレッドの特性です。

実行待ち:
 これは、実行準備が整っていて実行可能なスレッドの状態です。
 この状態のスレッドは、「スクリプトの実行権」が自分に移譲される
 のを待っています。
 複数のスレッドがこの状態にある時、待ち行列が形成されます。
 この行列は、早い者順に並びます。後から来た者は最後に並びます。
 この並び順が、「スクリプトの実行権」移譲のデフォールトの優先順位に
 なります。この順とは無関係に、特定のスレッドに実行権を移譲すること
 もできます。

睡眠中:
 これは、所定の時間が経過するまで、あるいは、所定のイベントが
 発生するまで、待っているスレッドの状態です。
 この状態のスレッドが複数ある時、待ち行列が形成されます。
 この行列は、待ち時間の短い順に並びます。しかし、必ずしも
 この順に待ち状態が解消されるとは限りません。
 この待ち状態が解消されたスレッドは、いきなり「実行中」になる
 のではなく、一旦「実行待ち」の状態になります。

●スレッドの動作
 まず、スクリプトを最初に実行する時点で、「メインスレッド」が生成
されます。メインスレッドは、「暗黙のメイン関数」をコールします。
その動作中に、メインスレッドは、他のスレッドを任意に生成することが
できます。もし、他のスレッドを生成しなければ、シングルスレッドの動作で
終わります。

 どのスレッドでも、他のスレッドを幾つでも生成できます。スレッドには、
生成した方と生成された方で、その親子関係上の生得的な差異はありません。
どのスレッドも平等に扱われます。

 スレッドが生成されると、そのスレッドが、いきなり「実行中」の状態に
なるわけではなく、まず「実行待ち」の状態になります。その際は,上記の
待ち行列の最後に並ぶことになります。一方、そのスレッドを生成した方の
親スレッドは、その「実行中」の状態を継続します。

 スクリプトの実行権は、現「実行中」のスレッドが、自ら、次のどれかを
行なった時に初めて、切り換わります。
  ・自身の実行を終了する
  ・明示的に実行権を他に移譲する
  ・睡眠に入る
  ・イベント待ち状態になる

 スレッドが生成される時には、通常、最初にコールすべき関数とその引数が
指定されます。つまり、スレッドの実行は、その関数コールで始まります。
一方、その関数を抜けた時(リターンした時)に、そのスレッドの実行が
終了します。

 各スレッドは、それぞれ、固有の「識別番号」が割り当てられています。
この値は、スレッドが生成された時に確定し、生涯変わりません。
特定のスレッドに対する操作を行なう時には、この識別番号で指定します。

 メインスレッドが終了しても、他のスレッドが残っていれば、スクリプトの
実行は継続します。全てのスレッドの実行が終了した時に、スクリプトの実行
が終了します。

●スレッドの協調
 一般に、マルチスレッド環境では、各スレッドの協調動作ができる必要が
あります。例えば、あるスレッドが自分の作業をしながら、他のスレッドに
別の作業を依頼して、それが完了した時点で、その結果を使うというような
場合です。

 このような動作を行なうには、各スレッドの同期が必要で、そのために、
  セマフォ、クリティカルセクション、イベント、ミューテックス等
の機能が、一般的には備わっています。

 本マルチスレッドの環境では、これらの機能は、「イベントキュー」等で
提供しています。これに関しては、後程説明します。

●スレッド操作関数
 スレッド関連の操作は、本言語では、以下のリレー型関数で行ないます。

 'start 関数
機能: 対象の関数をスレッドとして起動します。
書式: <関数名>'start( <引数の並び> )
返値: 起動したスレッドの識別番号( 起動失敗時は、null )
説明: 通常の関数コールの書式では、
      <関数名>( <引数の並び> )
    となりますが、関数をスレッドとして起動する場合は、上記のように、
    <関数名> と ( <引数の並び> ) の間に、'start を入れた書式になります。
    もし、その関数に引数がなければ、'start はリレー型関数なので、
      <関数名>'start
    のように、引数を省略しても構いません。
    'start を実行すると、新規にスレッドが生成されます。
    このスレッドの「識別番号」が、'start 関数の返値になります。
    新規生成されたスレッドは、即時に実行権を獲得するのではなく、
    まず「実行待ち」状態の待ち行列の最後に並びます。
    'start を実行したスレッド自身は、そのまま実行を継続します。

    起動したスレッドに、その場で実行権を移譲したい場合は、
      <関数名>'start( <引数の並び> )'yield;
    とします。'yield に関しては、後で説明しています。
    起動したスレッドを、T [ms] 時間経過するまで休止させる場合は、
      <関数名>'start( <引数の並び> )'sleep( T );
    とします。'sleep に関しては、後で説明しています。
用例:
        print "メインスレッド・スタート!";
        ^IsToStop = #FALSE;   // サブスレッドへの終了要求OFF
      
        // サブスレッドとして、Thread という関数を起動
        Thread'start( "サブスレッド・スタート!" );
      
        // メッセージボックス表示
        ::Inform( "メインスレッドが、このボックスを表示して、\n"
                  "サブスレッドが、裏でプリントしています。\n"
                  "このボックスを閉じると、\n"
                  "メインスレッドは、すぐに終了しますが、\n"
                  "サブスレッドは、しばらくしてから終了します。" );
      
        ^IsToStop = #TRUE;    // サブスレッドへの終了要求ON
        print "メインスレッド終了!";
        return;
      
        function  Thread( msg )     // この関数はサブスレッドで実行
        {
            print msg;    // メインスレッドからのメッセージをプリント
      
            // 0, 1, 2, 3, ... と順に数字をカウントアップしてプリント
            for( i = 0 ; i <= 1000 ; i++ )
            {
                print i;
                'sleep( 100 );
                if( ^IsToStop )   // メインスレッドから終了要求あり?
                    break;
            }
            print "サブスレッドは、もうすぐ終了します...";
            //(注意)メインスレッドが終了しても、サブスレッドは継続
      
            // 10, 9, 8, ... と順に数字をカウントダウンしてプリント
            for( i = 10 ; i >= 0 ; i-- )
            {
                print i;
                'sleep( 100 );
            }
            print "サブスレッド終了!";
        }
'stop 関数 機能: 対象のスレッドの実行を(強制的に)終了します。 書式: <識別番号>'stop 説明: 'stop 関数を実行すると、その対象の <識別番号> に対応する     スレッドの実行が(強制的に)終了します。     <識別番号> が省略された場合、'stop を実行したスレッド自身の     実行が終了します。 返値: 終了したスレッドの識別番号( 失敗時は、null )     ( Ver1.24 以下では、成功時 1, 失敗時 0 ) 注意: スレッドの実行終了は、通常、そのスレッドが起動された時に、     最初にコールした関数から抜ける(リターンする)ことによって、     行ないます。そうすれば、その関数のローカルスコープ内にクラスの     インスタンスが残っていても、そのデストラクタが、通常通りにコール     されます。しかし、'stop で、スレッドの実行を強制終了した場合、     そのスレッドが使用していた全メモリーは正常に解放されますが、     そのローカルスコープ内にあるインスタンスのデストラクタは、     コールされません。 用例: 
        // サブスレッドを起動して、3 秒後に強制終了する
        Tid = Func'start;   // サブスレッドを起動
        'sleep( 3000 );     // 3 秒間休止
        Tid'stop;           // サブスレッドを終了
        return;

        function  Func()    // スレッドとして実行される関数
        {
            for( i = 1 ;; i++ )   // プリントを繰り返す
            {
                print i;
                'yield;    // ← 実行権の切り換えに必要!
            }
        }
'sleep 関数 機能: 対象のスレッドを指定時間だけ睡眠させます。 書式: <識別番号>'sleep( <時間> ) 説明: 'sleep は、<識別番号> に対応するスレッドを「睡眠中」の状態に     移行させます。そのスレッドが既に睡眠中の場合は、その睡眠時間を、     引数の <時間> に更新します。     <識別番号> が省略された場合、'sleep を実行したスレッド自身が     「睡眠中」の状態に移行します。たぶん大半の用途がこの場合でしょう。     引数の <時間> には、睡眠時間(単位:ミリ秒)を指定します。     <時間> を省略した場合、永久睡眠になります。     <時間> の値が 0 以下の場合、睡眠時間が既に経過済みと見なされて、     即時に起床します。(起床に関しては下記の注意参照)     'sleep を実行したスレッドは、その睡眠対象が自分以外の場合、     そのまま実行を継続します。     'sleep を実行したスレッド自身が睡眠した場合、実行権は、     「実行待ち」行列の先頭のスレッドに移譲されます。もし、その     スレッドがなければ、全スレッドが「睡眠中」ということになります。 補説: 「睡眠中」のスレッドは、睡眠時間が経過すると起床します。その際、     即時に実行権を獲得するのではなく、まず「実行待ち」行列の最後に     回されます。その時、もし他に「実行待ち」のスレッドも「実行中」の     スレッドもなければ、起床したスレッドが、実行権を獲得します。 注意: 「DLLのコールバック関数」内で、'sleep は実行できません。 返値: 睡眠したスレッドの識別番号( 失敗時は、null )     ( Ver1.24 以下では、成功時 1, 失敗時 0 ) 用例: 
            function  Func( x )  {  print x ;  }

            print "メイン開始";
            Func'start( "Thread (1)" )'sleep( 3000 );
            Func'start( "Thread (2)" )'sleep( 2000 );
            Func'start( "Thread (3)" )'sleep( 1000 );
            'wait;   // 全サブスレッドの終了を待つ
            'sleep( 1000 );
            print "メイン終了";
    このスクリプトを実行すると、次の各行が1秒経過するごとに     ぽつぽつとプリントされます。       メイン開始       Thread (3)       Thread (2)       Thread (1)       メイン終了 'wake 関数 機能: 対象の睡眠中のスレッドを強制的に起床させます。 書式: <識別番号>'wake 説明: 'wake 関数を実行すると、その対象の <識別番号> に対応する     睡眠中のスレッドを(強制的に)起床させます。     <識別番号> が省略された場合、全ての睡眠中のスレッドを     (強制的に)起床させます。     起床させられたスレッドは、「実行待ち」行列の最後に並びます。     'wake を実行したスレッド自身は、そのまま実行を継続します。 返値: 起床したスレッドの識別番号( 失敗時は、null )     ( Ver1.24 以下では、成功時 1, 失敗時 0 ) 注意: ダイアログ入出力待ち( ::Input, ::Confirm, ::Inform, ::Alert の     どれかの関数を実行中)のスレッドに対して、'wake はできません。 用例: 
            function  Func( x )
            {
                print x : " 開始";
                'sleep;  // 永久睡眠
                print x : " 終了";
            }

            a = Func'start( "A" )'yield;
            b = Func'start( "B" )'yield;
            a'wake'yield;   // Aを起こして実行権を移譲
            b'wake'yield;   // Bを起こして実行権を移譲
    このスクリプトを実行すると、次のようにプリントされます。       A 開始       B 開始       A 終了       B 終了 'wait 関数 機能: 対象のスレッドの実行が終了するまで待ちます。 書式: <識別番号>'wait( <時間> ) 説明: 'wait を実行したスレッドは、<識別番号> に対応するスレッドの     実行が終了するまで、睡眠状態で待機します。     引数の <時間> は、タイムアウト時間(単位:ミリ秒)を指定します。     <時間> の値が正の場合、それが待ち時間の限度になります。もし、     その時間が経過しても、対象のスレッドの実行が終了していなければ、     つまり、タイムアウトになると、待機中のスレッドは、睡眠状態を     解除されて、「実行待ち」行列の最後に回されます。     <時間> を省略した場合、永久に待ち続けます。     <時間> の値が 0 以下の場合、即時にタイムアウトになります。     <識別番号> が省略された場合、自分以外の全スレッドの実行が終了     するまで待機します。但し、これが行なえるのは、メインスレッド     だけです。 注意: どのスレッドも、メインスレッドの実行終了を待つことはできません。     また、既に実行終了待ちをしているスレッドの実行終了を待つことは     できません。また、自分自身の実行終了を待つこともできません。     「DLLのコールバック関数」内で、'wait は実行できません。 返値: = 1: 対象スレッドの実行終了待ちが完了して、正常に復帰     = 0: 失敗(対象スレッドの実行終了待ちができなかった場合)     =-1: タイムアウト(対象スレッドの実行が終了する前に、        引数の <時間> が経過してしまった場合) 用例: 
        function  A()  {  print "A開始";  'sleep( 3000 );  print "A終了";  }
        function  B()  {  print "B開始";  'sleep( 2000 );  print "B終了";  }
        function  C()  {  print "C開始";  'sleep( 1000 );  print "C終了";  }

        print "メイン開始";
        A'start();  B'start();  C'start();
        'wait();    // 全サブスレッドの終了待ち
        print "メイン終了";
    このスクリプトを実行すると、次のようにプリントされます。       メイン開始       A開始       B開始       C開始       C終了       B終了       A終了       メイン終了 'yield 関数 機能: 対象のスレッドに実行権を移譲します。 書式: <識別番号>'yield 説明: 'yield を実行すると、まず、そのスレッドの実行権がなくなります。     その際、そのスレッドは、「実行待ち」行列の最後に回されます。     次に、<識別番号> に対応するスレッドが、「実行待ち」行列内から     検索されて、もし見つかれば、そのスレッドに実行権が移譲されます。     見つからないか、または、<識別番号> が省略された場合には、     「実行待ち」行列の先頭のスレッドに、実行権が移譲されます。     なお、「睡眠中」のスレッドに、実行権を移譲することはできません。 返値: 移譲先スレッドの識別番号( 失敗時は、null )     ( Ver1.24 以下では、成功時 1, 失敗時 0 ) 注意: 「DLLのコールバック関数」内で、'yield は実行できません。 用例: 
        ^A = A_Thread'start;
        ^B = B_Thread'start;
        return;

        function  A_Thread()
        {
            for( i = 1 ; i <= 3 ; i++ )
            {
                print "A: " : i;
                ^B'yield;
            }
        }
        function  B_Thread()
        {
            for( i = 1 ; i <= 5 ; i++ )
            {
                print "B: " : i;
                ^A'yield;
            }
        }
    ちなみに、これを実行すると、次の通りプリントされます。      A: 1      B: 1      A: 2      B: 2      A: 3      B: 3      B: 4      B: 5 'ticks 関数 機能: 対象のスレッドの現時点までの実行時間の合計を取得します。 書式: <識別番号>'ticks 説明: 'ticks は、<識別番号> に対応するスレッドが、誕生してから     現時点までに、実行権を獲得していた時間の合計を返します。     <識別番号> が省略された場合、'ticks を実行したスレッド自身が     対象になります。 返値: 実行時間の合計(単位がミリ秒の整数値)     =-1: 失敗(対象スレッドがない場合) 注意: 本スクリプトのスレッドが実行権を獲得していても、実際には     OSが他のプロセスにCPUを割り当てている場合があります。     また、そもそも、本システムは、各スレッドの実行時間を厳密に     管理しているわけではありません。そのため、'ticks の返す     時間の精度はあまりよくありません。あくまで目安程度です。     なお、本時間は、ミリ秒以下が切り捨てになります。 用例: 次のスクリプトを実行すると、for ループを百万回実行するのに     要した時間がプリントされます。
            for( i = 0 ; i < 1`000`000 ; i++ )
                continue;
            print 'ticks;
'tid 関数 機能: 現スレッドの識別番号を返します。 書式: 'tid 説明: 本関数は、本関数を実行したスレッドの「識別番号」を返します。     ちなみに、これは整数値です。 ●実行制御関数  スクリプトの実行制御は、以下のリレー型関数で行ないます。 'Error! 関数 機能: 現スレッドを異常終了します。 書式: 'Error!( <項目>,... ) 説明: 本関数は、スクリプトの実行において、現スレッドのみを異常終了     させます。他のスレッドは、通常通りです。     'Error! は、自スレッドに対して 'stop を行なうのとほぼ同じですが、     次の違いがあります。     まず、'Error! では、スクリプトの実行上、異常があったことに     なります。本言語が、テキストエディタ等のアプリケーションに     組み込まれている場合、スクリプトの反復実行モードがありますが、     'Error! が実行されれば、その反復実行モードは解除されます。     次に、'Error! では、異常終了時に、引数で指定された各 <項目> を、     (エラーメッセージとして)プリントします。     引数は、なし、または、1個以上のプリント対象の <項目> を指定します。     引数がない場合、'Error! だけで、丸括弧は勿論不要です。     引数がある場合、各 <項目> のデータ型式は何であっても構いません。     文字列なら、そのままプリントします。     数値なら、適当な文字列に変換してプリントします。     それ以外なら、所定の形態の文字列に変換してプリントします。     このプリントでは、最後に自動的に改行が付加されることはありません。 注意: 「DLLのコールバック関数」内で、'Error! を実行すると、     コールバック関数自身の実行が終了します。この場合、他のスレッド     には影響しません。 返値: 無意味(そもそも本関数を実行したスレッドが終了してしまうため) 用例: 
        if( Result < 0 )
            'Error!( "不正な結果になりました!\n" );
    if(( file = ::File.Open( "ReadMe.txt", "in" )) == null )
            'Error!( ::ErrorMessage( $err_code, $err_info ), "\n" );
'Pause! 関数 機能: スクリプトの実行を一時停止します。 書式: 'Pause!( <項目>,... ) 説明: 本関数は、全スレッドの実行を一時停止することによって、     スクリプトの実行を一時停止します。     スクリプトの実行再開は、スクリプト自身では行なえません。     本言語が、テキストエディタ等のアプリケーションに組み込まれて     いる場合、実行再開のボタンを押すか、メニュー項目を選択します。     しかし、本言語が、コンソールアプリケーションとして実装されて     いる場合、実行再開の操作は通常できません。そのため、'Pause! は、     スクリプトでは実行しないほうが良いでしょう。     引数の <項目> があれば、一時停止する前に、プリントされます。     この引数に関しては、'Pause! の場合と同様です。 返値: 無効( null ) 'Abort! 関数 機能: スクリプトの実行を強制的に中止します。 書式: 'Abort!( <項目>,... ) 説明: 本関数は、全スレッドの実行を強制終了することによって、     スクリプトの実行を強制的に中止します。     引数の <項目> があれば、強制中断の前に、プリントされます。     この引数に関しては、'Pause! の場合と同様です。 注意: 本言語が、テキストエディタ等のアプリケーションに組み込まれて     いる場合、スクリプトの反復実行モードがありますが、     'Abort! を実行すると、その反復実行モードは解除されます。 返値: 無意味(そもそも本関数を実行したスレッドが終了してしまうため) 用例: 
        if( DoSomethig() != #OK )
            'Abort!( "異常発生!(%d)\n"'fmt( Result ) );
●イベントキュー操作関数  本スクリプト言語のマルチスレッド環境では、各スレッドの協調動作が できるように、「イベントキュー」を提供しています。  イベントキューでは、基本的に、あるスレッドがそこにイベントを 入れて、別のスレッドがそこから取り出すというような形態で使います。 このイベントは、各スレッド間で取り決めるもので、任意のデータが 使えます。ここでは、イベント(事象)と言っていますが、その意味に 限定して使う必要はありません。  イベントを取り出す側のスレッドは、イベントキューが空の時には、 イベントが入ってくるまで、睡眠状態で待つことができます。 その際には、最大待ち時間を設定することもできます。現実行中の スレッドが、この待ち状態になると、「実行待ち」行列の先頭の スレッドに、実行権が移譲されます。  イベントキューは、FIFO(First-In First-Out:先入れ先出し型 バッファ)としても、スタック(Last-In First-Out:後入れ先出し型 バッファ)としても、使えます。  イベントキューでは、整数値や文字列だけでなく、任意の構造の複合箱の 出し入れも可能です。また、1つのイベントキュー内に、異なるデータ型、 あるいは構造の箱があっても構いません。  イベントキューで箱を出し入れする際、箱の中身がコピーされるのではなく、 その箱自身が移動します。これは「移動代入」的な動作になります。 この移動は、コピーよりも、かなり高速で、メモリーの使用量も少なくて 済みます。  イベントキューを使えば、イベント駆動型のプログラミングができます。 また、イベントキューを、セマフォや、ミューテックスとして、使うことも できます。  イベントキューは、マルチスレッド環境で使うように設計されていますが、 シングルスレッドでも、FIFOやスタックとして使うことができます。 また、イベントキュー自身は、複合箱の一種なので、箱としての一般的な 操作が可能です。  次に、イベントキューを操作するリレー型関数を説明します。 'queue! 関数 機能: 対象の箱をイベントキュー用の箱に設定します。 書式: <箱名>'queue! 説明: 本関数は、<箱名> で指定された対象の箱をイベントキュー用の箱に     します。     対象の箱が存在していない場合、その箱を新規に生成します。     対象の箱が既存の複合箱の場合、その中身はそのまま引き継ぎます。     対象の箱が既存の単一箱の場合、その中身は破棄して、その箱を、     空のイベントキュー用の箱に変更します。 返値: 正常終了時、イベントキュー用に設定した対象の箱への一時参照     不正終了時、null( <箱名> の指定が不正の場合等 ) 用例: グローバルスコープに EventQue というイベントキュー用の箱を     作成するには、 ::EventQue'queue!;     とします。ちなみに、当箱が既存でその中が空でない可能性がある     時に、初期状態として必ず空を保証するには、 ::EventQue'new!'queue!;     とします。 'push と 'post 関数 機能: 対象の箱内に、指定項目の箱を追加します。 書式: <箱名>'push( <項目>, ... )     <箱名>'post( <項目>, ... ) 説明: 'push は、対象の箱内の先頭に指定項目を追加します。     'post は、対象の箱内の末尾に指定項目を追加します。     両関数の違いは、これ以外には特にありません。     <箱名> で指定された対象の箱が存在していない場合、       その箱名で、空の複合箱を新規に生成します。     対象の箱が既存の単一箱の場合、       その中身を破棄して、その箱を空の複合箱に変更します。     対象の箱が既存の複合箱の場合、       そのまま使用します。     追加する <項目> は、複数個指定しても構いません。     指定された <項目> が、箱の場合、       その箱は、対象の箱内に移動します。その結果、元の場所から       その箱が無くなっているので、注意が必要です。なお、<項目> に       指定する箱は、移動可能ならどのような箱でも構いません。     指定された <項目> が、直値(数値/文字列リテラル等)の場合、       その値を保持する箱が、対象の箱内に追加生成されます。     いずれの場合でも、対象の箱内に追加された箱の名前は、       適当な一時名になります。これは通常、気にする必要はありません。 補説: 'push と 'pop を使えば、スタック操作が行なえます。     'post と 'pop を使えば、FIFO操作が行なえます。     対象の箱は、必ずしも、イベントキュー用の箱でなくても構いません。     また、本関数の実行によって、対象の箱がイベントキュー用の箱に     変わるわけではありません。     対象の箱がイベントキュー用の箱の場合、本関数の実行の最後で、     そのイベントキュー待ちのスレッドがあるかどうかが確認されます。     もしあれば、そのスレッドは、イベント待ちの状態を解除されて、     「実行待ち」行列の最後に並びます。     ちなみに、本関数コール時に <項目> の指定がなかった場合、何も     追加されませんが、その時点でイベントキューが空でなければ、     上記のイベントキュー関連の処理が行なわれます。 返値: 正常終了時、対象の箱への一時参照     不正終了時、null( <箱名> の指定が不正の場合等 ) 用例: (1) FIFOとして、整数値を出し入れする場合
              Q'post(1), Q'post(2), Q'post(3);
              Q'post(4,5,6);
              while(( v = Q'pop ) != null )
                  print v, -;
              print;
     これを実行すると、次のようにプリントされます。        1, 2, 3, 4, 5, 6,     (2) スタックとして、文字列を出し入れする場合
              Q'push("A");  Q'push("B");  Q'push("C");
              Q'push("F","E","D");
              while(( v = Q'pop ) != null )
                  print v, -;
              print;
     これを実行すると、次のようにプリントされます。        F, E, D, C, B, A,     (3) 注意
              for( i = 1 ; i <= 6 ; i++ )
                  Q'post( i );    // ← ※
      これは、上記(1)の例と同じ 'post 結果になりそうですが、       ※ の実行時に、変数 i の箱が、Q の箱内に移動されて、       元の場所には無くなるので、i++ の時に例外が発生します。       これを避けるには、例えば、 Q'post( i'val );       とします。または、 msg = i; Q'post( msg );       のようにしても構いません。 'pop 関数 機能: 対象の箱内の先頭の箱を取り出します。 書式: <箱名>'pop( <時間> ) 説明: 本関数は、<箱名> で指定された対象の箱内にある先頭の箱を、     取り出します。この取り出しに関しては、下記の「補説」で     もう少し詳しく説明しています。     対象の箱が、複合箱でなければ、この取り出しに失敗します。     対象の箱が、複合箱であって、その中が空でなければ、通常、     この取り出しには成功します。     対象の箱が、イベントキュー用の箱であれば、それが空であっても、     そこにイベントが入って来るまで待つことができます。     引数の <時間> は、そのタイムアウト時間(単位:ミリ秒)を指定します。     <時間> の値が正の場合、その時間が経過しても、イベントが入って     来なければ、つまり、タイムアウトになると、取り出しは失敗になります。     その際、そのスレッドは、待機状態を解除されて、「実行待ち」行列の     最後に並びます。     <時間> を省略した場合、イベントが入って来るまで、ずっと待ち続けます。     <時間> の値が 0 以下の場合、即時にタイムアウトになります。     これは、対象の箱が空か否かを確認する時に使います。     対象の箱がイベントキュー用の箱でなければ、<時間> の指定は無効です。     つまり、対象の箱が空なら、即時、取り出しに失敗します。 返値: 取り出しに成功した時、取り出した箱( 下記「補説」参照 )     取り出しに失敗した時、null 補説: 対象の箱内から取り出された箱は、ひとまず「一時箱」に移動されます。     この時、その箱は元の場所には無くなっています。本関数の返値は、     この一時箱を示す値になります。この一時箱は、通常の箱とほとんど     同様に扱われます。ただ、これが代入式の右辺になる場合、その代入は     必ず「移動代入」になります。つまり、代入演算子 = := <- が     どれであっても、一時箱の実体が代入先へ移動されます。これによって、     その一時箱は消えます。一時箱は、このようにして消えなくても、     使われなくなった時点で自動的に破棄されます。そのため、一時箱が     いつまでも残存することはありません。 注意: <時間> 指定がない場合、対象の箱がイベントキュー用の箱か否かで、     それが空の時の動作が異なるので注意が必要です。例えば、       Q'pop     では、Q がイベントキュー用の箱なら、ずっと待ち続けますが、     Q がイベントキュー用の箱でなければ、即時に失敗して戻ってきます。 用例: 次のスクリプトは、メインスレッドが、イベントキューに、イベントを     ポストし、サブスレッドがそれを取り出してプリントします。
        ::EventQue'new!'queue!;     // イベントキューを作成
        EventHandler'start();       // イベント受信スレッドを起動

        // イベントキューに、0, 1, ..., 9 の数値を 0.5 秒ごとにポスト
        for( n = 0 ; n < 10 ; n++ )
        {
            ::EventQue'post( n'val );
            'sleep( 500 );
        }
        ::EventQue'post( -1 );      // 終了を示すイベントをポスト
        return;

        function  EventHandler()    // イベント受信処理スレッド関数
        {
            do  // イベントキューからイベントを取り出してプリント
            {
                ev = ::EventQue'pop;    // イベントを受信するまで待つ
                print ev;
            }
            while( ev != -1 );
        }