首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在并行库中使用队列(公共Lisp)

在并行库中使用队列(公共Lisp)
EN

Stack Overflow用户
提问于 2019-03-23 21:37:23
回答 1查看 459关注 0票数 3

https://z0ltan.wordpress.com/2016/09/09/basic-concurrency-and-parallelism-in-common-lisp-part-4a-parallelism-using-lparallel-fundamentals/#channels中对队列的基本讨论说,队列“允许在工作线程之间传递消息”。下面的测试使用一个共享队列来协调一个主线程和一个从属线程,其中主线程只是在退出之前等待从属线程的完成:

代码语言:javascript
复制
(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))

这与预期的生产产出相同:

代码语言:javascript
复制
* (test)

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

我的问题是,我是正确地还是完全地使用了非并行的队列功能。队列似乎只是使用全局变量来保存线程共享对象的替代。使用队列的设计优势是什么?通常情况下,为每个提交的任务分配一个队列(假设任务需要通信)是否是一个好做法?谢谢你有更深入的见解。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-25 10:02:57

多线程工作是通过管理对可变共享状态的并发访问来完成的,也就是说,您在一个公共数据结构周围有一个锁,并且每个线程都对其进行读或写。

不过,建议尽量减少并发访问的数据数量。通过让每个线程管理其本地状态并仅通过消息交换数据,队列是一种使工作人员彼此解耦的方法;这是线程安全的,因为队列的访问是由锁和条件变量控制的。

您在主线程中所做的是在队列为空时进行轮询;这可能有效,但这会适得其反,因为队列被用作同步机制,但在这里您是自己进行同步的。

代码语言:javascript
复制
(ql:quickload :lparallel)
(defpackage :so (:use :cl
                      :lparallel
                      :lparallel.queue
                      :lparallel.kernel-util))
(in-package :so)

让我们更改foo,使其获得两个队列,一个用于传入请求,另一个用于答复。在这里,我们对正在发送的数据执行一个简单的转换,对于每个输入消息,只有一个输出消息,但这不需要总是这样。

代码语言:javascript
复制
(defun foo (in out)
  (push-queue (1+ (pop-queue in)) out))

更改test,以便控件流仅基于对队列的读/写:

代码语言:javascript
复制
(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))))

但是,如果有多个任务正在运行,那么如何避免在测试中进行轮询?您不需要不断地检查它们中的任何一个是否已经完成,这样就可以将更多的工作推送到它了吗?

您可以使用一种类似于民意调查/epoll的不同的并发机制,在这两种机制中,您可以监视多个事件源,并在其中一个事件准备就绪时作出反应。有些语言,如Go (选择)和Erlang (接收),这是很自然的表达方式。在Lisp方面,卡利佩尔库提供了类似的交互机制(pri-altfair-alt)。例如,以下内容取自Calispel的测试代码:

代码语言:javascript
复制
(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)))

在非平行的情况下,不存在这样的机制,但是只要您用标识符标记消息,就可以很好地处理队列。

如果您需要在任务t1t2给出结果时立即作出反应,那么将这两个任务写入相同的结果通道:

代码语言:javascript
复制
(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 ...))))

如果您需要同步t1t2发出结果的代码,请让它们在不同的通道中编写:

代码语言:javascript
复制
(let ((t1 (foo :id 1 :in i1 :out o1))
      (t2 (bar :id 2 :in i2 :out o2)))
   (list (pop-queue o1)
         (pop-queue o2)))
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55318645

复制
相关文章

相似问题

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