【发布时间】:2020-04-10 11:28:59
【问题描述】:
我有一些可能运行时间很长的函数,有时可能会挂断。所以,我想如果我把它包装到async 工作流中,那么我应该可以取消它。这是一个不起作用的 FSI 示例(但编译后的代码也会发生相同的行为):
open System.Threading
let mutable counter = 0
/// Emulates an external C# sync function that hung up.
/// Please, don't change it to some F# async stuff because
/// that won't fix that C# method.
let run() =
while true
do
printfn "counter = %A" counter
Thread.Sleep 1000
counter <- counter + 1
let onRunModel() =
let c = new CancellationTokenSource()
let m = async { do run() }
Async.Start (m, c.Token)
c
let tryCancel() =
printfn "Starting..."
let c = onRunModel()
printfn "Waiting..."
Thread.Sleep 5000
printfn "Cancelling..."
c.Cancel()
printfn "Waiting again..."
Thread.Sleep 5000
printfn "Completed."
#time
tryCancel()
#time
如果你在 FSI 中运行它,你会看到类似的东西:
Starting...
Waiting...
counter = 0
counter = 1
counter = 2
counter = 3
counter = 4
Cancelling...
Waiting again...
counter = 5
counter = 6
counter = 7
counter = 8
counter = 9
Completed.
Real: 00:00:10.004, CPU: 00:00:00.062, GC gen0: 0, gen1: 0, gen2: 0
counter = 10
counter = 11
counter = 12
counter = 13
counter = 14
counter = 15
counter = 16
这意味着在调用c.Cancel() 之后它根本不会停止。
我做错了什么以及如何使这样的事情起作用?
以下是一些附加信息:
- 当代码挂起时,它会在一些外部同步 C# 库中执行此操作,
我无法控制。所以检查取消令牌
我控制的代码没用。这就是为什么函数
run()上面就是这样建模的。 - 我不需要任何关于完成和/或进度的沟通。它已经通过一些消息传递系统完成并且超出了范围 的问题。
- 基本上,只要我“决定”这样做,我只需要终止后台工作。
【问题讨论】:
-
杀死是什么意思?
CanellationToken只是一个通知。如果该方法实际上并没有注册自己或检查取消它实际上没有做任何事情。 -
您正在使用 Tread.Sleep。试试 Async.Sleep。不确定,但我怀疑这会尊重取消。
-
没错。我正在尝试寻找一种可行的替代方法。例如。
t = new Thread(fun () -> run())... 然后调用t.Abort()确实会终止线程,但它会在 FSI 中发出一声巨响 - 整个 FSI 会话被终止。 -
Thread.Sleep是故意的。请参阅问题末尾的评论 #1。 -
我认为你根本不应该使用线程的东西。 Async 和 Task 不是线程的东西,尽管它们使用线程来完成工作。
标签: asynchronous f#