WorkerThread
メソッドの起動(invocation)と実行(execution)の分離(Thread-Per-MessageとWorker Thread) - qnzm.log(クニジマログ)をLispで。
なんかお題があると書きやすいな。qnzmネタにしてごめん。
Thread-Per-Messageは単にワーカーに投げるだけなので飛ばして、ThreadPoolだけ作った。
waitforはGuarded Suspension してみたと同じ。
今回の収穫は(中身に関係ないけど)eval-whenを知ったことと、このあたりを読んでふむふむ、と思える程度になってきたことに気がついたこと。
それから(こっちは中身に関係ある)initialize-instanceをオーバーライドすると、:initform
とか:initargでの初期化がされないので、:afterにすると学んだこと。
コードは以下
(defclass channel () ((thread-count :accessor thread-count :initarg :thread-count) (threads :accessor threads :initform nil) (workitem :accessor workitem :initform nil) (sm :accessor semaphore))) (defmethod initialize-instance :after ((c channel) &rest intargs) (if (not (slot-boundp c 'thread-count)) (setf (thread-count c) 3)) (setf (semaphore c) (sb-thread::make-semaphore :name (symbol-name (gensym)))) (sb-thread::signal-semaphore (semaphore c)) (dotimes (i (thread-count c)) (append (threads c) (list (sb-thread:make-thread #'(lambda () (print "...") (format t "start worker thread ~A~%" (sb-thread:thread-name sb-thread:*current-thread*)) (do () (nil 'DONE) (channel-process c)) (symbol-name (gensym)))))))) (defmethod channel-enqueue-workitem((c channel) function) (format t "enqueue workitem~%") (waitfor (semaphore c) (> (thread-count c) (length (workitem c))) (format t "append workitem~%") (setf (workitem c) (append (workitem c) (list function))) (format t "workitem count ~A~%" (length (workitem c))))) (defun channel-process (c) (format t "process called wait...~%") (waitfor (semaphore c) (< 0 (length (workitem c))) (format t "process workitem~%") (let ((wi (car (workitem c)))) (setf (workitem c) (cdr (workitem c))) (funcall wi)))) ::test (setf c (make-instance 'channel)) ;;(channel-start c) (setf *random-state* (make-random-state t)) (do () (nil 'NODE) (let ((cmd (read-line))) (channel-enqueue-workitem c #'(lambda () (format t "cmd ~A~%" cmd) (sleep (+ 3 (random 3 *random-state*)))))))