2014-07-05

SRFI-18の紹介

(LISP Library 365参加エントリ)

SRFI-18はマルチスレッドを扱うライブラリです。POSIXが採用しているスレッドモデルとほぼ同じなのでなれている人はほぼ同じように使えます(条件変数の扱いが多少違うのと、ミューテックスが破棄された状態を持つのが違う点かと思います)。

使い方を見てみましょう。以下は簡単なスレッドの作成です。
(import (srfi :18))
(let ((t (thread-start! (make-thread (lambda () (write 'a))))))
  (write 'b)
  (thread-join! t))
;; -> <unspecified>
;; prints ab or ba
スレッドの作成にはmake-thread、開始にthread-start!、終了を待つのにthread-join!を使います。

スレッドを使っていると数秒止めたくなることもあるでしょう。そんなときにはthread-sleep!を使います。
(import (srfi :18))
(thread-sleep! 1) ;; sleep 1 second
一応スレッドの停止(破棄)もサポートされています。このオペレーションは非常に危険なので注意して使えという注釈も付いています。
(thread-terminate! (current-thread)) ;; -> never return
thread-yieldは他のスレッドに処理時間を明け渡す手続きです。非常に重たい処理を行うスレッドが可能な限りCPUの空き時間に処理を行いたい場合などに使えます。
(import (rnrs) (srfi :18))

(define m (make-mutex))
(define (do-something-else) (print 'something-else) (thread-sleep! 1))

(let ((t (make-thread (lambda ()
                        ;; a busy loop that avoids being too wasteful of the CPU
                        (let loop ()
                          (if (mutex-lock! m 0) ; try to lock m but don't block
                              (begin
                                ;; Do some heavy process
                                (display "locked mutex m")
                                (mutex-unlock! m))
                              (begin
                                (do-something-else)
                                (thread-yield!) ; relinquish rest of quantum
                                (loop))))))))
  (mutex-lock! m 0)
  (thread-start! t)
  (thread-sleep! 1)
  (mutex-unlock! m))
僕自身はそんなミッションクリティカルな処理を書いたことが無いので使ったことのない手続きの一つですが・・・

次にミューテックス及び条件変数の使い方を見てみましょう。ミューテックスを扱う手続きは既に上記の例で出ていますが、ロックを書けるのにはmutex-lock!、外すのにはmutex-unlock!を使います。また、このSRFIで採用しているモデルでは条件変数はシグナルを送る以外の手続きを持っていないので、pthreadのpthread_cond_waitのようなことをするにはmutex-unlock!を使います。mutex-unlock!のオプショナル引数が条件変数を受け取った場合には、pthread_cond_waitとほぼ同様の動作をします。ただし、ミューテックスはロックされていないので、注意が必要です。

今回はSRFI-18を紹介しました。

No comments:

Post a Comment