首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在竞争条件下利用SBCL提供的信号量

如何在竞争条件下利用SBCL提供的信号量
EN

Stack Overflow用户
提问于 2013-07-31 10:33:47
回答 2查看 871关注 0票数 6

据我对信号量的了解,信号量被用来保护资源,这些资源可以被计算,并且容易受到种族条件的影响。但是,在阅读信号量的SBCL文档时,我想不出如何正确地使用提供的信号量实现来保护资源。

我记得,通常的工作流程是:

  1. 一个进程希望通过信号量保护的数据来检索其中的一些数据(为了示例起见,这是一个琐碎的队列)。由于信号量计数器为0,进程将等待。
  2. 另一个进程在队列中放置一些东西,当信号量增加时,一个信号被发送给所有等待的进程。

考虑到交错的可能性,我们必须保护这些资源访问中的任何一个,因为它们可能不是按照这个顺序,或者任何线性顺序。因此,Java将每个类解释为隐式监视器,并提供一个syncronized关键字,程序员可以用该关键字定义一个保护区域,每次只能由一个进程访问。

如何模仿这个通用的lisp功能,因为我确信我当前的代码和没有信号量的线程安全一样安全,因为信号量不知道要保护什么代码。

代码语言:javascript
运行
复制
;;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)
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-07-31 19:03:24

您已经拥有了一个计数= 0的信号量,消费者在此等待。

您还需要的是访问堆栈的专用锁(可能是每个堆栈一个),或者是一个无锁队列。如果希望/必须使用信号量,二进制信号量可以充当独占锁。

编辑:在SBCL中,您已经拥有了无锁队列,您可能需要使用其中的一个而不是两个堆栈。另一种可能是使用原子运算

最后,如果这仍然不适合您,请使用互斥包装代码,以访问和更新with-mutexwith-recursive-lock中的堆栈。

确保在从信号量中唤醒之后使用锁/互斥量,而不是在等待信号量的周围使用,否则您将失去信号量给您带来的优势,这就是连续唤醒多个服务员的可能性,而不是一次唤醒一个。

您可以在SBCL手册中阅读有关这些内容的所有内容。

此外,我认为已经做了一些工作,将SBCL中的每一个类似锁的东西重命名为lock,但我不知道它的状态,它声明旧名称将在一段时间内得到支持。

您几乎肯定还需要一个用于生产者的计数=限制的信号量,以不超过您的队列限制。

enqueue-*中,您应该在更新队列后向信号量发出信号。不需要setfpush已经将列表的新头存储在适当的位置。

在您的dequeue-*中,当应用于列表时,length是一个冗长的函数,但是使用nullendp检查列表是否为空很便宜。与使用car和存储cdr不同,您可以使用pop,它就是这样做的。

票数 3
EN

Stack Overflow用户

发布于 2013-08-01 06:20:26

在队列操作期间,您需要持有互斥信号量(也称为'mutex')。使用这样的SBCL互斥体:

代码语言:javascript
运行
复制
(defclass thread-queue ()
  ((lock :initform (sb-thread:make-mutex :name 'thread-queue-lock))
   ...))

(defmethod enqueue-* ((queue thread-queue) element)
  (sb-thread:with-recursive-lock ((slot-value queue 'lock))
    (setf (slot-value queue 'in-stack) (push element (slot-value queue 'in-stack)))))

* (defvar lock (sb-thread:make-mutex))
LOCK

* lock
#S(SB-THREAD:MUTEX
   :NAME NIL
   :%OWNER NIL
   :LUTEX #<unknown pointer object, widetag=#x5E {11CEB15F}>)

* (sb-thread:with-recursive-lock (lock) 'foo)    
FOO

* (sb-thread:with-recursive-lock (lock) (sb-thread:with-recursive-lock (lock) 'foo))
FOO

据推测,with-recursive-lock宏将对非本地出口执行正确的操作(使用unwind-protect或其他类似的方式解锁)。

这相当于Java,上面的synchronized保护enqueue-*方法;您需要对所有其他可以异步调用的方法执行此操作。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17968018

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档