首页 > 解决方案 > Racket协程实现中的死锁

问题描述

作为一个帮助我理解 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 '())))

标签: functional-programmingschemeracketcoroutinecontinuations

解决方案


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

这在调用时返回它作为对象的延续。如果你看这段代码:

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

的延续(make-proc-args '())letwith currentbound和proccalled的应用。在以下情况下:

(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)再次调用.. let 以iand开头0.. 但是人们会期望存储的延续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不是在 a 的主体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)

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


推荐阅读