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*)))))))