就我对信号量的了解而言,信号量用于保护可计数且易受竞争条件影响的资源。但是在阅读信号量的 SBCL 文档时,我无法弄清楚如何正确使用提供的信号量实现来保护资源。
我记得一个通常的工作流程是:
一个进程想要检索一些受信号量保护的数据(就示例而言,这是一个简单的队列)。由于信号量计数器为0,进程等待
另一个进程将某些东西放入队列中,随着信号量的增加,一个信号被发送到所有等待的进程
考虑到交错的可能性,必须保护这些资源访问中的任何一个,因为它们可能不是按那个顺序,或者根本不是任何线性顺序。因此,例如,Java 将每个类解释为隐式监视器,并提供一个syncronized
关键字,程序员可以使用该关键字定义一次只能由一个进程访问的受保护区域。
如何在 common-lisp 中模拟此功能,因为我很确定我当前的代码在没有信号量的情况下是线程安全的,因为信号量不知道要保护什么代码。
;;the package
(defpackage :tests (:use :cl :sb-thread))
(in-package :tests)
(defclass thread-queue ()
((semaphore
:initform (make-semaphore :name "thread-queue-semaphore"))
(in-stack
:initform nil)
(out-stack
:initform nil)))
(defgeneric enqueue-* (queue element)
(:documentation "adds an element to the queue"))
(defgeneric dequeue-* (queue &key timeout)
(:documentation "removes and returns the first element to get out"))
(defmethod enqueue-* ((queue thread-queue) element)
(signal-semaphore (slot-value queue 'semaphore))
(setf (slot-value queue 'in-stack) (push element (slot-value queue 'in-stack))))
(defmethod dequeue-* ((queue thread-queue) &key timeout)
(wait-on-semaphore (slot-value queue 'semaphore) :timeout timeout)
(when (= (length (slot-value queue 'out-stack)) 0)
(setf (slot-value queue 'out-stack) (reverse (slot-value queue 'in-stack)))
(setf (slot-value queue 'in-stack) nil))
(let ((first (car (slot-value queue 'out-stack))))
(setf (slot-value queue 'out-stack) (cdr (slot-value queue 'out-stack)))
first))
(defparameter *test* (make-instance 'thread-queue))
(dequeue-* *test* :timeout 5)
(enqueue-* *test* 42)
(enqueue-* *test* 41)
(enqueue-* *test* 40)
(dequeue-* *test* :timeout 5)
(dequeue-* *test* :timeout 5)
(dequeue-* *test* :timeout 5)
(dequeue-* *test* :timeout 5)