Multithreading 使用Lparallel库中的队列(公共Lisp)

Multithreading 使用Lparallel库中的队列(公共Lisp),multithreading,common-lisp,message-queue,Multithreading,Common Lisp,Message Queue,在的lparallel库中对队列的基本讨论是,队列“支持工作线程之间的消息传递”。下面的测试使用共享队列来协调主线程和从属线程,其中主线程只是在退出之前等待从属线程的完成: (defun foo (q) (sleep 1) (lparallel.queue:pop-queue q)) ;q is now empty (defun test () (setf lparallel:*kernel* (lparallel:make-kernel 1)) (let ((c (lpar

在的lparallel库中对队列的基本讨论是,队列“支持工作线程之间的消息传递”。下面的测试使用共享队列来协调主线程和从属线程,其中主线程只是在退出之前等待从属线程的完成:

(defun foo (q)
  (sleep 1)
  (lparallel.queue:pop-queue q))  ;q is now empty

(defun test ()
  (setf lparallel:*kernel* (lparallel:make-kernel 1))
  (let ((c (lparallel:make-channel))
        (q (lparallel.queue:make-queue)))
    (lparallel.queue:push-queue 0 q)
    (lparallel:submit-task c #'foo q)
    (loop do (sleep .2)
             (print (lparallel.queue:peek-queue q))
          when (lparallel.queue:queue-empty-p q)
            do (return)))
  (lparallel:end-kernel :wait t))
这与预期的生产结果一致:

* (test)

0
0
0
0
NIL
(#<SB-THREAD:THREAD "lparallel" FINISHED values: NIL {10068F2B03}>)
*(测试)
0
0
0
0
无
(#)

我的问题是,我是否正确或完全使用了lparallel的队列功能。队列似乎只是使用全局变量来保存线程共享对象的替代品。使用队列的设计优势是什么?为每个提交的任务分配一个队列(假设任务需要通信)通常是一种好的做法吗?感谢您的深入见解。

多线程工作是通过管理对mutable的并发访问来完成的 共享状态,即在公共数据结构周围有一个锁, 每个线程都会读或写

但是,建议尽量减少正在处理的数据数量 同时访问。排队是一种将工人与每个人分离的方法 其他方法是让每个线程管理其本地状态并交换数据 只有通过信息;这是线程安全的,因为访问 队列由控制

您在主线程中所做的是轮询队列何时停止 是空的;这可能会起作用,但会适得其反,因为队列 用作同步机制,但这里您正在执行 你自己同步

(ql:quickload :lparallel)
(defpackage :so (:use :cl
                      :lparallel
                      :lparallel.queue
                      :lparallel.kernel-util))
(in-package :so)
让我们更改
foo
,这样它将获得两个队列,一个用于传入队列 请求,一个请求回复。这里,我们执行一个简单的转换来 发送的数据和每个输入消息都有一个 输出消息,但不一定总是这样

(defun foo (in out)
  (push-queue (1+ (pop-queue in)) out))
更改
test
,使控制流仅基于对队列的读取/写入:

(defun test ()
  (with-temp-kernel (1)
    (let ((c (make-channel))
          (foo-in (make-queue))
          (foo-out (make-queue)))
      (submit-task c #'foo foo-in foo-out)
      ;; submit data to task (could be blocking)
      (push-queue 0 foo-in)
      ;; wait for message from task (could be blocking too)
      (pop-queue foo-out))))

但若有多个任务正在运行,那个么如何避免在测试中进行轮询呢?您不需要连续检查其中任何一项是否完成,以便将更多工作推送到队列吗

您可以使用不同的并发机制,类似于和,其中您可以监视多个 事件的来源,并在其中一个事件准备就绪时作出反应。有像Go()和Erlang()这样的语言 这是很自然的表达。在Lisp端,库提供了类似的替换机制(
pri-alt
fair-alt
)。例如,以下代码取自Calispel的测试代码:

(pri-alt ((? control msg)
          (ecase msg
            (:clean-up (setf cleanup? t))
            (:high-speed (setf slow? nil))
            (:low-speed (setf slow? t))))
         ((? channel msg)
          (declare (type fixnum msg))
          (vector-push-extend msg out))
         ((otherwise :timeout (if cleanup? 0 nil))
          (! reader-results out)
          (! thread-expiration (bt:current-thread))
          (return)))
在lparallel的情况下,没有这样的机制,但是只要用标识符标记消息,就可以只使用队列

如果您需要在任务
t1
t2
给出结果时立即做出反应,则让这两个任务在同一结果通道中写入:

(let ((t1 (foo :id 1 :in i1 :out res))
      (t2 (bar :id 2 :in i2 :out res)))
   (destructuring-bind (id message) (pop-queue res)
     (case id
       (1 ...)
       (2 ...))))
如果您需要在
t1
t2
发出结果时同步代码,请让它们在不同的通道中写入:

(let ((t1 (foo :id 1 :in i1 :out o1))
      (t2 (bar :id 2 :in i2 :out o2)))
   (list (pop-queue o1)
         (pop-queue o2)))

那么,队列大致类似于lambda函数列表?也就是说,函数可以将任意lisp对象(作为参数)传递给从属函数,线程可以将任意lisp对象(作为“out”队列元素)传递给另一个线程。然后,接收线程可以使用“in”队列元素初始化或以其他方式执行其任务(或阻塞,直到元素可用为止)。接收线程还可以通过使用其“out”队列请求额外的数据,并在其“in”队列上接收数据。最终,接收线程可以通过使用其“out”队列报告其结果(或者在lparallel中通过其通道返回值)。这有意义吗?但若有多个任务正在运行,那个么如何避免在测试中进行轮询呢?您不需要不断检查其中任何一项工作何时完成,以便将更多工作推送到队列吗?简单的解释!你一定是一名CS教授。我想我现在会坚持使用lparallel,但了解其他人(魂器问题)很好。谢谢!祝你好运