Asynchronous 为什么不能立即取消Async.Sleep?
下面的测试表明F#2.0中的Async.Sleep不能立即取消。只有在时间过后,我们才会收到“取消”通知Asynchronous 为什么不能立即取消Async.Sleep?,asynchronous,f#,Asynchronous,F#,下面的测试表明F#2.0中的Async.Sleep不能立即取消。只有在时间过后,我们才会收到“取消”通知 module async_sleep_test open System open System.Threading open System.Threading.Tasks open System.Xml let cts = new CancellationTokenSource() Task.Factory.StartNew(fun () -
module async_sleep_test
open System
open System.Threading
open System.Threading.Tasks
open System.Xml
let cts = new CancellationTokenSource()
Task.Factory.StartNew(fun () ->
try
Async.RunSynchronously(async{
printfn "going to sleep"
do! Async.Sleep(10000)
}, -1(*no timeout*), cts.Token)
printfn "sleep completed"
with
| :? OperationCanceledException ->
printfn "sleep aborted" // we will see it only after 10 sec.
| _ ->
printfn "sleep raised error"
) |> ignore
Thread.Sleep(100) // give time to the task to enter in sleep
cts.Cancel()
Thread.Sleep(100) // give chance to the task to complete before print bye message
printfn "press any key to exit...."
Console.ReadKey(true) |> ignore
我认为这是不正确的行为。你认为这是一个怎样的错误?例如,如果我将使用以下实现,是否会有任何惊喜:
static member SleepEx(milliseconds:int) = async{
let disp = new SerialDisposable()
use! ch = Async.OnCancel(fun()->disp.Dispose())
do! Async.FromContinuations(fun (success, error, cancel) ->
let timerSubscription = new SerialDisposable()
let CompleteWith =
let completed = ref 0
fun cont ->
if Interlocked.Exchange(completed, 1) = 0 then
timerSubscription.Dispose()
try cont() with _->()
disp.Disposable <- Disposable.Create(fun()->
CompleteWith (fun ()-> cancel(new OperationCanceledException()))
)
let tmr = new Timer(
callback = (fun state -> CompleteWith(success)),
state = null, dueTime = milliseconds, period = Timeout.Infinite
)
if tmr = null then
CompleteWith(fun ()->error(new Exception("failed to create timer")))
else
timerSubscription.Disposable <- Disposable.Create(fun()->
try tmr.Dispose() with _ -> ()
)
)
}
静态成员SleepEx(毫秒:int)=异步{
让disp=new SerialDisposable()
使用!ch=Async.OnCancel(fun()->disp.Dispose())
do!Async.FromContinuations(乐趣(成功、错误、取消)->
let timerSubscription=new SerialDisposable()
让CompleteWith=
let completed=ref 0
乐趣继续->
如果联锁。交换(完成,1)=0,则
timerSubscription.Dispose()
用->()尝试cont()
一次性用品
CompleteWith(fun()->cancel(新操作CanceledException()))
)
设tmr=新计时器(
回调=(乐趣状态->完成(成功)),
state=null,dueTime=毫秒,period=Timeout.Infinite
)
如果tmr=null,则
CompleteWith(fun()->错误(新异常(“未能创建计时器”))
其他的
时间订阅。一次性
尝试将tmr.Dispose()与->()
)
)
}
我不会说这是一个bug——它源于F#异步工作流中通常处理取消的方式。通常,F#假设您使用调用的基本操作让代码>或执行
不支持取消(我想在.NET中没有标准的取消机制),因此F#在使用let进行呼叫之前和之后插入取消检查代码>
所以一个电话让我来!res=foo()
当然,由foo()
返回的工作流可以更好地处理取消-通常,如果它是使用async{..}
块实现的,那么它将围绕每个let代码>。但是,通常(除非某些操作以更巧妙的方式实现),取消将在下一次let代码>调用完成
你的Sleep
的替代定义在我看来相当不错-它比F#library中提供的定义更支持取消,如果你需要立即取消,那么用你的SleepEx
替换F#的Async.Sleep
是唯一的方法。但是,可能仍然会有一些操作不支持immediate取消,因此您可能会在其他地方遇到问题(如果您在任何地方都需要这种行为)
PS:我认为你的SleepEx
功能对其他人可能非常有用。如果你能在网上分享,那就太棒了
我认为这是不正确的行为。你认为这是一个怎样的错误
是的,我还以为是虫子呢。我把它当作臭虫报告了。微软同意这是一个bug。他们已经修复了F#3.0/VS2012中的错误,以及TryScan和其他程序中的错误。NB F#>=3的睡眠将正确地执行取消,通过取消执行线程,作为静态成员SleepCorrect(x:TimeSpan)=async{let!ct=async.CancellationToken return!System.threading.Tasks.Task.Delay(x,ct)|>Async.AwaitTaskCorrect}
就可以了
token.ThrowIfCancellationRequested()
let! res = foo()
token.ThrowIfCancellationRequested()