F# 中的合作取消与取消继续
Cooperative cancellation in F# with cancel continuation
可能我这里有 2 个问题而不是一个,但无论如何。
我正在实施 here suggested 合作取消。这是我的测试代码:
type Async with
static member Isolate(f : CancellationToken -> Async<'T>) : Async<'T> =
async {
let! ct = Async.CancellationToken
let isolatedTask = Async.StartAsTask(f ct)
return! Async.AwaitTask isolatedTask
}
let testLoop (ct: CancellationToken) = async {
let rec next ix =
if ct.IsCancellationRequested then ()
else
printf "%i.." ix
Thread.Sleep 10
next (ix+1)
next 1
}
let cancellationSource = new CancellationTokenSource()
let onDone () = printfn "!! DONE"
let onError _ = printfn "!! ERROR"
let onCancel _ = printfn "!! CANCEL"
Async.StartWithContinuations (Async.Isolate testLoop, onDone, onError, onCancel, cancellationSource.Token)
Thread.Sleep(100)
cancellationSource.Cancel ()
Thread.Sleep(500)
如您所见,我从 done、cancel 和 error continuations 开始异步.如果我 运行 按原样使用该代码,我将得到以下输出:
1..2..3..4..5..6..7..8..!! DONE
如果我稍微更新Isolate
方法如下:
static member Isolate(f : CancellationToken -> Async<'T>) : Async<'T> =
async {
let! ct = Async.CancellationToken
let isolatedTask = Async.StartAsTask(f ct)
let! x = Async.AwaitTask isolatedTask
x
}
我得到了预期的(我自己)输出:
1..2..3..4..5..6..7..!! CANCEL
为什么我们在行为上会有如此不同?
是否可以中止 testLoop
,如果它在一段时间内没有取消?
async
块仅在绑定前后检查 Async.CancellationToken
的取消(使用 let!
编写)。这意味着当令牌被取消时,工作流只会在有更多工作要做时被取消。
同样值得注意的是 isolatedTask
本身在这个例子中并没有被取消,因为它只是定期终止(使用 if
)。
你的情况:
当您仅使用 return!
时,任务 returns 定期执行,Async.AwaitTask
returns 定期执行,之后什么都不做,因此工作流程完成。
当您使用 let!
后接 return
时,任务 returns 定期和 Async.AwaitTask
returns 定期,但随后 let!
在 运行 return
之前检查取消,这会取消工作流。
可能我这里有 2 个问题而不是一个,但无论如何。
我正在实施 here suggested 合作取消。这是我的测试代码:
type Async with
static member Isolate(f : CancellationToken -> Async<'T>) : Async<'T> =
async {
let! ct = Async.CancellationToken
let isolatedTask = Async.StartAsTask(f ct)
return! Async.AwaitTask isolatedTask
}
let testLoop (ct: CancellationToken) = async {
let rec next ix =
if ct.IsCancellationRequested then ()
else
printf "%i.." ix
Thread.Sleep 10
next (ix+1)
next 1
}
let cancellationSource = new CancellationTokenSource()
let onDone () = printfn "!! DONE"
let onError _ = printfn "!! ERROR"
let onCancel _ = printfn "!! CANCEL"
Async.StartWithContinuations (Async.Isolate testLoop, onDone, onError, onCancel, cancellationSource.Token)
Thread.Sleep(100)
cancellationSource.Cancel ()
Thread.Sleep(500)
如您所见,我从 done、cancel 和 error continuations 开始异步.如果我 运行 按原样使用该代码,我将得到以下输出:
1..2..3..4..5..6..7..8..!! DONE
如果我稍微更新Isolate
方法如下:
static member Isolate(f : CancellationToken -> Async<'T>) : Async<'T> =
async {
let! ct = Async.CancellationToken
let isolatedTask = Async.StartAsTask(f ct)
let! x = Async.AwaitTask isolatedTask
x
}
我得到了预期的(我自己)输出:
1..2..3..4..5..6..7..!! CANCEL
为什么我们在行为上会有如此不同?
是否可以中止 testLoop
,如果它在一段时间内没有取消?
async
块仅在绑定前后检查 Async.CancellationToken
的取消(使用 let!
编写)。这意味着当令牌被取消时,工作流只会在有更多工作要做时被取消。
同样值得注意的是 isolatedTask
本身在这个例子中并没有被取消,因为它只是定期终止(使用 if
)。
你的情况:
当您仅使用
return!
时,任务 returns 定期执行,Async.AwaitTask
returns 定期执行,之后什么都不做,因此工作流程完成。当您使用
let!
后接return
时,任务 returns 定期和Async.AwaitTask
returns 定期,但随后let!
在 运行return
之前检查取消,这会取消工作流。