Racket协程实现中的死锁

Deadlock in Racket co-routine implementation

作为一个帮助我理解 Racket 中的延续的项目,我决定尝试编写一个不使用可变或全局变量的协程实现。到目前为止,这是我所拥有的,但它似乎最终陷入了某种僵局。我是否漏掉了一些明显的东西?

#!/usr/bin/env racket

#lang racket

(define (make-proc-args args)
  (let/cc cc
    (cons cc args)))

(define (fork proc)
  (let ([current (make-proc-args '())])
    (proc current)))

(define (yield to args)
  (let ([current (make-proc-args args)])
    ((car to) current)))

(define c
  (fork
    (lambda (p)
      (let loop ([i 0]
                 [parent p])
        (unless (> i 10)
          (loop (+ i 1) (yield parent (list i))))))))

(let loop ([child c])
  (println (car child))
  (loop (yield child '())))
(define (make-proc-args args)
  (let/cc cc
    (cons cc args)))

这在调用时 returns 它是作为对象的延续。如果您查看此代码:

(define (fork proc)
  (let ([current (make-proc-args '())])
    (proc current)))

(make-proc-args '()) 的延续是 letcurrent 绑定和 proc 调用的应用。在上下文中:

(fork
 (lambda (p)
   (let loop ([i 0]
              [parent p])
     (unless (> i 10)
       (loop (+ i 1) (yield parent (list i)))))))

这意味着 (yield parent (list i)) 将时间旅行回来并调用 (proc current) 将再次调用..让以 i0 开头..但是一个会期望 yield 的延续被存储,对吧?错了!

(define (yield to args)
  (let ([current (make-proc-args args)])
    ((car to) current)))

捕获的延续是((car to) current),它恰好一次又一次地相同。

解决此问题的最简单方法是使延续不调用存储的延续,因为它是自己的延续。因此你需要做这样的事情:

(define (fork proc)
  (let/cc cc
    (let ([current (cons cc '())])
      (proc current))))

(define (yield to args)
  (let/cc cc
    (let ([current (cons cc args)])
      ((car to) current))))

请注意,在这两个中,延续是 yieldfork returns 自然发生的,而不是 let 的主体完成时发生的。

还知道延续在顶层被分隔,所以您也许应该测试 let 块中的所有代码以捕获您可能遇到的错误,因为延续在顶层的行为不同。 define 不允许在顶层,但如果将它放在 let 中,你会得到 #<void> 作为最后一个值 child 因为那是值 define 形式,而不是你期望的对。

(define (worker p)
  (let loop ([i 0]
             [parent p])
    (unless (> i 10)
      (loop (+ i 1) (yield parent i)))))

(let ((c (fork worker)))
  (let loop ([child c])
    (when (pair? child)
      (println child)
      (loop (yield child '())))))

这会打印:

(#<continuation> . 0)
(#<continuation> . 1)
(#<continuation> . 2)
(#<continuation> . 3)
(#<continuation> . 4)
(#<continuation> . 5)
(#<continuation> . 6)
(#<continuation> . 7)
(#<continuation> . 8)
(#<continuation> . 9)
(#<continuation> . 10)

作为最后的提示。也许你应该为你的延续对象创建一个结构或者至少是抽象的?