2013-07-31 3 views
6

세마포에 대한 지식이있는 한 세마포는 계산할 수 있고 경쟁 조건에 취약한 리소스를 보호하는 데 사용됩니다. 그러나 세마포어에 대한 SBCL 문서를 읽는 동안 제공된 세마포어 구현을 사용하여 리소스를 보호하는 방법을 알 수 없었습니다.SBCL이 경쟁 조건에 대해 제공 한 세마포어를 사용하는 방법

자주 수행 작업 흐름, 나는 것 기억으로 :

  1. 프로세스는 세마포어로의 일부를 검색하고 싶어 (예 사소한 큐의 이익을 위해입니다) 데이터를 보호. 세마포어 카운터가 0으로, 과정은

  2. 다른 프로세스가 큐에 무언가를두고 세마포어가 증가 같이, 신호가 가능성을 감안할 때 모든 대기 프로세스

로 전송 대기 인터리빙을 위해서는 자원 순서가 아닐 수도 있고 선형 순서가 아닐 수도 있으므로 자원 액세스를 보호해야합니다. 따라서. 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) 

답변

3

이미 보유한 것은 count = 0 인 세마포어로 소비자가 대기합니다.

당신이 필요로하는 것은 스택 (아마도 각각 하나씩)에 접근하는 배타적 인 잠금 장치 또는 잠금없는 큐입니다. 세마포어를 사용하고 싶거나 사용해야하는 경우, 바이너리 세마포어가 배타적 잠금이 될 수 있습니다.


편집 : SBCL에서 , 이미 lock-free queues을 가지고, 당신은 두 스택 대신에 이들 중 하나를 사용할 수도 있습니다. 또 다른 가능성은 atomic operations입니다. 그것은 여전히 ​​당신을 적합하지 않는 경우

마지막으로, acesses 및 with-mutex 또는 with-recursive-lock 내부 스택을 업데이트하는 mutex, 포장 코드를 사용합니다.

그렇지 않으면 여러 웨이터를 깨어 난의 가능성이 세마포어가 당신을 제공하는 장점을 잃게의 세마포어를 기다리는하지 주위, 세마포어에서 깨어 난 후 잠금/뮤텍스 을 사용하십시오 한 번에 하나씩이 아니라 연속적으로

SBCL manual에서 이러한 모든 내용을 읽을 수 있습니다.

this blog post에 따르면 SBCL의 모든 자물쇠 모양의 이름을 lock으로 바꾼 일부 작업이 있지만 그 상태를 알 수는 없으며 이전 이름이 지원 될 것이라고 생각합니다. 잠시.


당신은 거의 확실 또한 큐 제한을 초과하지 않도록, 카운트 생산자 = 제한의 세마포어가 필요합니다.

enqueue-* 큐를 업데이트 한 후 세마포를 신호해야합니다. setf은 필요하지 않으며 push은 이미 목록의 새 헤드를 저장합니다. 목록에 적용하지만, 목록이 비어있는 경우 확인하는 null 또는 endp 저렴한입니다 때 dequeue-*에서

length는 긴 기능입니다. car을 가져 가서 cdr을 저장하는 대신 pop을 사용하면됩니다.

+0

잘 세마포어에 자물쇠를 사용하는 것이 두 가지 가능성을 열어줍니다 : (a) 프로세스가 세마포어를 기다리지 만 외부를 반환하지 않으면 교착 상태가 발생할 수 있으므로 매우 위험합니다. lock (b) 자물쇠 밖에서 세마포를 사용하십시오. 그렇다면 아무 것도 얻을 수없는 멋진 카운터를 얻었습니다. 나는 주어진 구현이 코드를 보호하는 깔끔한 방법을 제공하기를 희망했다. 그러한 가능성이없는 경우. 내 발언 (a)에서 편집하십시오. 나는 대답으로 받아 들일 것입니다. (당신이 다른 물건에 대해서도 맞습니다.) – Sim

+0

@Sim, 오케이, 나는 내 대답에 당신의 말을 추가 할 것입니다. 또한, 세마포어를 사용하여 무언가를 얻으십시오 : 수동 대기 및 여러 웨이터를 연속적으로 깨우기. – acelent

+0

@Sim, 처음 단락에 몇 단어를 추가하여 더 명확하게했습니다. 이전 버전에서는 독점 리소스 액세스에 대한 잠금을 사용하는 것이 세마포어 대신 사용되었다는 인상을 줄 수있었습니다. – acelent

1

대기열 작업 기간 동안 상호 제외 세마포어 (일명 '뮤텍스')를 보유해야합니다. 같은 SBCL 뮤텍스를 사용

(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 또는 일부 등을 사용하여 잠금을 해제).

위 내용은 메서드를 보호합니다. 비동기 적으로 호출 할 수있는 다른 모든 메소드에이 작업을 수행해야합니다.