2013-07-31 38 views
6

Theo như kiến ​​thức của tôi về semaphores đi, một semaphore được sử dụng để bảo vệ tài nguyên có thể được tính và dễ bị điều kiện chủng tộc. Nhưng trong khi đọc tài liệu của SBCL về các semaphores tôi không thể tìm ra, làm thế nào để sử dụng đúng cách thực hiện semaphore được cung cấp để bảo vệ tài nguyên.Làm thế nào để sử dụng SBCL cung cấp semaphore chống lại điều kiện chủng tộc

Một luồng công việc thường lệ, khi tôi gọi lại sẽ là:

  1. một quá trình muốn lấy một số các bằng semaphore bảo vệ dữ liệu (đó là vì lợi ích của ví dụ một hàng đợi tầm thường). Khi semaphore đếm là 0, quá trình chờ đợi

  2. quá trình khác đặt một cái gì đó trong hàng đợi và như semaphore là tăng lên, một tín hiệu được gửi đến tất cả các quá trình chờ đợi

Với khả năng xen kẽ, người ta phải bảo vệ bất kỳ truy cập tài nguyên nào như chúng có thể không theo thứ tự đó, hoặc bất kỳ thứ tự tuyến tính nào cả. Vì vậy, ví dụ: Java diễn giải từng lớp dưới dạng một giám sát ngầm và cung cấp một từ khóa syncronized mà một lập trình viên có thể định nghĩa một khu vực được bảo vệ mà chỉ có thể được truy cập bởi một tiến trình tại một thời điểm.

Làm cách nào để mô phỏng chức năng này theo ngôn ngữ chung, vì tôi khá chắc chắn mã hiện tại của tôi là an toàn chủ đề không có semaphore, vì semaphore không có đầu mối để bảo vệ.

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

Trả lời

3

Những gì bạn đã có là một semaphore count = 0, mà người tiêu dùng chờ đợi.

Những gì bạn cũng cần là một khóa độc quyền xung quanh truy cập ngăn xếp của bạn (có thể là một ngăn xếp) hoặc một hàng đợi không có khóa. Nếu bạn muốn/phải sử dụng semaphores, một semaphore nhị phân có thể phục vụ như là một khóa độc quyền.


EDIT: Trong SBCL, bạn đã có lock-free queues, bạn có thể muốn sử dụng một trong những thay vì hai ngăn xếp. Một khả năng khác là sử dụng atomic operations.

Cuối cùng, nếu điều đó vẫn không phù hợp với bạn, hãy sử dụng mã số gói mutex để tiếp tục và cập nhật ngăn xếp bên trong with-mutex hoặc with-recursive-lock.

Hãy chắc chắn để sử dụng khóa/mutex sau thức dậy từ semaphore, không xung quanh chờ đợi cho các semaphore, nếu không bạn sẽ mất lợi thế mà semaphores cung cấp cho bạn, đó là khả năng thức dậy nhiều người phục vụ liên tiếp, thay vì một lần.

Bạn có thể đọc tất cả những điều này trong số SBCL manual.

Ngoài ra, tôi nghĩ rằng một số công việc đã được thực hiện để đổi tên mọi thứ giống như khóa trong SBCL thành lock, theo số this blog post, nhưng tôi không biết trạng thái của nó và nói rằng tên cũ sẽ được hỗ trợ một lúc.


Bạn gần như chắc chắn cũng cần một semaphore đếm = giới hạn cho nhà sản xuất, không vượt quá giới hạn hàng đợi của bạn.

Trong số enqueue-*, bạn nên báo hiệu semaphore sau khi cập nhật hàng đợi. Không cần setf, push đã lưu trữ đầu mới của danh sách tại chỗ.

Trong dequeue-* của bạn, length là một hàm dài khi áp dụng cho danh sách, nhưng kiểm tra nếu một danh sách rỗng là rẻ với null hoặc endp. Thay vì lấy car và lưu trữ cdr, bạn có thể sử dụng pop, nó thực hiện chính xác điều đó.

+0

cũng sử dụng khóa bổ sung cho semaphore sẽ mở ra hai khả năng: (a) sử dụng semaphore bên trong khóa -> rất nguy hiểm vì điều này có thể dẫn đến bế tắc nếu quá trình chờ semaphore nhưng không trả về bên ngoài khóa (b) sử dụng semaphore bên ngoài khóa -> tốt, sau đó tôi đã đạt được gì, nhưng một truy cập ưa thích tôi có thể đã thực hiện bản thân mình. Tôi hy vọng triển khai được cung cấp sẽ cung cấp một cách gọn gàng để bảo mật mã. Nếu không có khả năng như vậy. Xin vui lòng chỉnh sửa trong nhận xét của tôi (a) và tôi sẽ chấp nhận nó như là một câu trả lời. (Bạn có quyền về những thứ khác) – Sim

+0

@Sim, ok, tôi sẽ thêm nhận xét của bạn vào câu trả lời của tôi. Ngoài ra, lưu ý rằng bạn có được một cái gì đó với semaphores: thụ động chờ đợi và đánh thức nhiều bồi bàn liên tiếp. – acelent

+0

@Sim, tôi đã thêm một vài từ vào các đoạn ban đầu để làm cho chúng rõ ràng hơn. Phiên bản trước đó có thể tạo ấn tượng rằng việc sử dụng khóa để truy cập tài nguyên độc quyền là một thay thế cho các ẩn dụ. – acelent

1

Bạn cần giữ một semaphore loại trừ lẫn nhau (còn gọi là 'mutex') trong suốt thời gian hoạt động của hàng đợi. Sử dụng một mutex SBCL như vậy:

(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 

Có lẽ các with-recursive-lock vĩ mô sẽ làm điều đúng (unlock khóa, sử dụng unwind-protect hoặc một số ví dụ) cho một lối ra phi địa phương.

Điều này tương đương với Java synchronized - phần trên bảo vệ phương thức enqueue-*; bạn cần phải làm điều đó với mọi phương thức khác có thể được gọi là không đồng bộ.

Các vấn đề liên quan