For Development HEAD DRAFTSearch (procedure/syntax/module):

12.19 data.queue - キュー

Module: data.queue

キュー(FIFO)機能を提供します。 極めて軽量ですがスレッドセーフでないシンプルなキューと、 スレッドセーフなmtqueueがあります。基本的なキュー操作手続きは どちらのキューに対しても使うことができます。本節で説明されている 手続きをmtqueueに適用した場合、特に断りが無い限り、操作は アトミックに行われます。

また、スレッド間同期に使えるmtqueue用の手続きも用意されています。 例えばキューが空の時に読み出しスレッドをブロックさせたり キューの長さが指定値に達した場合に書き込みスレッドをブロックさせることができ、 いわゆる「チャネル」としてキューを使うことができます。

シンプルキューのAPIはSLIBのキュー実装の上位互換になっています。 要素を並びの末尾に追加するenqueue!と、要素を並びの先頭から取り除く dequeue!だけでなく、要素を並びの先頭に追加するqueue-push!も 提供されているので、スタックとしても使えます。

要素を並びの末尾からO(1)で取り除くAPIも欲しい場合は、 deque (double-ended queue)が必要です。data.ring-bufferモジュール は、ベクタの上に実装された(空間的にも時間的にも)効率の良いdequeを提供しています (data.ring-buffer - リングバッファ参照)。また、変更不可な両端キューdata.ideque もあります(data.ideque - 変更不可な両端キュー参照)。

また、scheme.list-queueはポータブルなリストベースのキューのAPIを提供しています (scheme.list-queue - R7RSリストキュー)。


12.19.1 キューのクラスとコンストラクタ

Class: <queue>

{data.queue} シンプルなキューのクラスです。

Instance Variable of <queue>: length

キュー中の要素の数を返す、読み取り専用のスロットです。

Class: <mtqueue>

{data.queue} mtqueueのクラスです。<queue>を継承しています。

Instance Variable of <mtqueue>: max-length

キュー中の要素数の上限を示します。

このスロットが0の場合、キューは要素を中に持つことはできませんが、同期デバイスとして動作します。 書き込みスレッドは、その要素を受け取る読み出しスレッドが現れるまでブロックし、 読み出しスレッドは、要素を渡してくれる書き込みスレッドが現れるまでブロックします。

Instance Variable of <mtqueue>: closed

論理値のフラグ。初期値は#fです。 これが真になると、enqueue!などによる新たなデータの挿入ができなくなります。 このスロットは読み出し専用で、enqueue/wait!queue-push/wait!dequeue/wait!queue-pop/wait!によってアトミックにセットすることが できます。 これは、mtqueueをチャネルとして使っていて、シャットダウンする場合に有用です。

Function: make-queue

{data.queue} 空のシンプルなキューを作って返します。

Function: make-mtqueue :key max-length

{data.queue} 空のmtqueueを作って返します。整数がmax-lengthに与えられた場合は、 それがmax-lengthスロットの値となります。

Function: copy-queue queue

{data.queue} キューqueueのコピーを返します。queueがmtqueueなら、 同じ内容と最大長を持つ新たなmtqueueが返されます。


12.19.2 キューに関する述語

Function: queue? obj

{data.queue} objがキューであれば(シンプルなキューでもmtqueueでも)#tを返します。

Function: mtqueue? obj

{data.queue} objがmtqueueであれば#tを返します。

Function: queue-empty? queue

{data.queue} objが空のキューであれば#tを返します。


12.19.3 基本的なキュー操作

Function: enqueue! queue obj :optional more-objs …

{data.queue} objをキューqueueの末尾に追加します。 一つ以上のobjを与えることができ、その場合はそれらが順にenqueueされます。

queueがmtqueueの場合、渡されたオブジェクト全ての追加は アトミックに行われます。すなわち、途中に別スレッドが要素を挿入する ことはありません。さらに、max-lengthスロットが正の有限値を 持っており、このenqueue!の実行によってキューの要素数が max-lengthを越えることになる場合は、queueは 変更されずエラーとなります。 (max-lengthがゼロの場合、この手続きは常にエラーとなります。 enqueue/wait!を使ってください。チャネルとしてのキュー参照。)

queueがmtqueueで既にクローズされていた場合は、キューは変更されず エラーが投げられます。

Function: queue-push! queue obj :optional more-objs …

{data.queue} objをキューqueueの先頭に追加します。 一つ以上のobjを与えることができ、その場合はそれらが順にpushされます。

enqueue!と同様に、queueがmtqueueの場合は 全てのオブジェクトはアトミックに追加され、またmax-lengthclosedの値も チェックされます。詳しくは上のenqueue!の説明を参照してください。

Function: enqueue-unique! queue eq-proc obj :optional more-objs …
Function: queue-push-unique! queue eq-proc obj :optional more-objs …

{data.queue} objが既にqueueの中に含まれている場合にはqueueを 変更しないことを以外には、enqueue!およびqueue-push!と同じ 動作をします。objが含まれているかどうかの検査は 2引数の関数eq-procで行います。

queueがmtqueueの場合は 全てのオブジェクトはアトミックに追加され、またmax-lengthclosedの値も チェックされます。詳しくは上のenqueue!の説明を参照してください。

Function: dequeue! queue :optional fallback
Function: queue-pop! queue :optional fallback

{data.queue} キューqueueの先頭からひとつ要素を取って返します。 二つの手続きは全く同じ動作をします。queue-pop!queue-push!と ペアで使われていることを強調したいときに使うと良いでしょう。

キューが空の場合は、fallbackが与えられていればそれが返され、 そうでなければエラーが報告されます。

キューがmtqueueでそのmax-lengthがゼロの場合、 キューは常に空であるとみなされます。ゼロ長のキューを同期デバイスとして 使う場合はdequeue/wait!を使ってください。

Function: dequeue-all! queue

{data.queue} キューの全ての内容をリストにして返します。キューそのものは空になります。 キューが既に空の場合は空リストが返されます。 queue->listも参照してください(その他のキューのアクセサ)。

Function: remove-from-queue! pred queue

{data.queue} キューから、述語predを満たす要素を全て取り除きます。 要素が削除された場合は#tが、そうでなければ#fが返されます。 引数の順序はscheme.listremoveに揃えました (scheme.list - R7RSリスト参照)。

移植性に関する註:Scheme48には、述語ではなく削除するオブジェクトそのものを取る delete-from-queue!がありますが、引数の順序が逆(キューが先)になっています。 まぎらわしい衝突を避けるため、敢えてdelete-from-queue!は 提供しませんでした。remove-from-queue!を使えば、Scheme48互換の方法でも、 あるいはSRFI-1と一貫性のある方法でもdelete-from-queue!を簡単に書けるでしょう。


12.19.4 その他のキューのアクセサ

Function: queue-length queue

{data.queue} キューの中にある要素の数を返します。

Function: mtqueue-max-length mtqueue

{data.queue} キューが保持できる要素の最大数を返します。限度がない場合は#fが返ります。

Function: mtqueue-room mtqueue

{data.queue} 保持できる最大容量に達するまであといくつ要素を受け入れることができるかを 示す整数を返します。例えば、既にキューがいっぱいであれば0が返ります。 キューに最大容量が設定されていなければ+inf.0が返ります。

この手続きが0以外有限値を返した場合でも、続くenqueue!の呼び出し時点で キューがいっぱいになっている可能性があることに注意してください。 この手続きの呼び出しとenqueue!の 呼び出しの間に、他のスレッドがキューに要素を入れるかもしれないからです。 有限長のmtqueueに確実に要素を挿入したい場合はenqueue/wait!を使ってください。

Function: mtqueue-num-waiting-readers mtqueue

{data.queue} mtqueueからの読み出しで待っているスレッドの数を返します。 返り値は常に非負の正確な整数です。

この手続きが値を返してからその値を使うまでの間に、別のスレッドが キューに要素を挿入したら、待っているスレッドの数は変わってしまうことに 注意してください。この手続きの返り値を安全に使うには、 キューに値を挿入する部分を別に排他制御する必要があります。

(define q (make-mtqueue))

(thread-start! (make-thread (^[] (dequeue/wait! q))))

(mtqueue-num-waiting-readers q) ⇒ 1

(enqueue! q 'a)

(mtqueue-num-waiting-readers q) ⇒ 0
Function: queue-front queue :optional fallback
Function: queue-rear queue :optional fallback

{data.queue} キューqueueの先頭もしくは末尾の要素を返します。キューそのものは変更されません。 キューが空の場合、fallbackが与えられていればその値を返し、 そうでなければエラーを報告します。

Function: list->queue list :optional class :rest initargs

{data.queue} 与えられたリストlistの各要素をその順で持つようなキューを作成して返します。

デフォルトではシンプルキューが作られますが、class<class>の サブクラスを渡すことで他のキュークラスのインスタンスを作ることができます。 initargsclassのコンストラクタに渡されます。

Function: queue->list queue

{data.queue} キューqueueの内容をリストにして返します。 dequeue-all!と異なり、キューそのものの内容は変化しません。

Gaucheではqueue->listは新しいリストをアロケートしてキューの 内容をコピーします (dequeue-all!はコピーをせずにキューの内部の リストをそのまま返します)。組込みでqueue->listを持っているScheme 実装がいくつかありますが、その中にはqueue->listがキューの 内容をコピーすることを保証していないものがあるので、それらの処理系と 共有するコードではqueue->listがリストをコピーすることを あてにしない方が良いでしょう。

Function: find-in-queue pred queue

{data.queue} キュー内の要素のうち述語predを満たす最初の要素を返します。 引数の順序はfindに揃えました (他のリスト手続き参照)。

Function: any-in-queue pred queue

{data.queue} SRFI-1のanyのように、queue中の各要素に 順にpredを適用し、それが真の値 (#tである必要はありません) に 評価されたらその値を返します。predを満たす要素が無ければ#fが 返ります。

Function: every-in-queue pred queue

{data.queue} SRFI-1のeveryのように、queue中の各要素に 順にpredを適用し、それが#fを返したらすぐ#fを返します。 predに対し#fを返す要素が無ければ、最後の要素にpredを 適用した結果が返ります。キューが空なら#fが返ります。


12.19.5 チャネルとしてのキュー

Mtqueueはスレッドを使った生産者-消費者パターンの並行動作に使えます。 生産者がenqueue/wait!でキューに要素を挿入しようとして キューが一杯であった場合、消費者が要素を取り出すまで待ちます。 消費者がdequeue/wait!でキューから要素を取り出そうとして キューが空であった場合、生産者が要素を挿入するまで待ちます。

また、これらの手続きにはタイムアウトを設定することができ、 スレッドがスタックした場合に対応できます。

Function: enqueue/wait! mtqueue obj :optional timeout timeout-val close
Function: queue-push/wait! mtqueue obj :optional timeout timeout-val close

{data.queue} objmtqueueに挿入します。 enqueue!/queue-push!と同様に、 enqueue/wait!objをキューの末尾に (従って、既にある要素が全て取り除かれてからobjが取られます)、 queue-push/wait!objをキューの先頭に (従って、objは次のdequeueで取られます)、それぞれ挿入します。

キューが既に一杯である場合、呼び出しはブロックします。 キューから要素が取り出されて空きができるか、タイムアウトした場合に これらの手続きは戻ります。

timeout引数で、ブロックされたスレッドのタイムアウトを指定することができます。 #fは指定しなかった場合と同じで、キューの状態が変化するまで 無期限に待ちます。実数が渡された場合はそれが秒数と解釈され、最低限 その時間経過するまでは待ちます。渡されたのが<time>オブジェクト (時間参照)である場合は、そのオブジェクトが指定する絶対時刻を 経過するまで待ちます。

タイムアウトによりブロックが解かれた場合は、timeout-valで 指定した値が返されます。timeout-valの省略時値は#fです。

enqueue/wait!queue-push/wait!は、 タイムアウトせずに操作が成功した場合は#tを返します。

最後の省略可能引数closeは、もし真の値が与えられれば、キューをクローズします。 クローズはアトミックに行われるので、 enqueue/wait!queue-push/wait!を呼び出したのであれば、 objが最後にキューに追加されるデータであることが保証されます。 これはチャネルの「シャットダウン」操作と考えることができます。

もしmtqueueが既にクローズされていたらエラーが投げられます。 キューは変更されません。 フラグのチェックとキューへの挿入はアトミックに行われるので、 チェックと挿入の間に他のスレッドがenqueueしてしまうことはありません。

Function: dequeue/wait! mtqueue :optional timeout timeout-val close
Function: queue-pop/wait! mtqueue :optional timeout timeout-val close

{data.queue} キューの先頭から要素を取り出して返します。 どちらの手続きも全く同じ動作ですが、 dequeue/wait!enqueue/wait!と対で、 queue-pop/wait!queue-push/wait!と対で使うとわかりやすいでしょう。

呼び出しスレッドは、キューが空であった場合ブロックします。 キューに要素が追加されるか、タイムアウトが起きたら戻ってきます。

timeouttimeout-valclose引数については 上のenqueue/wait!を参照してください。

Function: mtqueue-close! mtqueue

{data.queue} mtqueueに要素を追加することなくキューをクローズします。 キューが既にクローズされていた場合は何もしません。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT