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 '())
的延续是 let
与 current
绑定和 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)
将再次调用..让以 i
和 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))))
请注意,在这两个中,延续是 yield
和 fork
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)
作为最后的提示。也许你应该为你的延续对象创建一个结构或者至少是抽象的?
作为一个帮助我理解 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 '())
的延续是 let
与 current
绑定和 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)
将再次调用..让以 i
和 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))))
请注意,在这两个中,延续是 yield
和 fork
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)
作为最后的提示。也许你应该为你的延续对象创建一个结构或者至少是抽象的?