这个程序工作正常:
let mutable inc =0
let a(o:obj)=
let autoEvent=o :?> AutoResetEvent
Console.WriteLine("a")
inc<-inc+1
if inc=3 then
autoEvent.Set()|>ignore
let autoEvent=new AutoResetEvent(false)
let timer=new Timer(a,autoEvent,0,2000)
autoEvent.WaitOne()|>ignore
但是当我想处理 tcp 客户端时,将相同的代码放在 async 块中时:
let mutable inc =0
let a(o:obj)=
let autoEvent=o :?> AutoResetEvent
Console.WriteLine("a")
inc<-inc+1
if inc=3 then
autoEvent.Set()|>ignore
let listener=new TcpListener(IPAddress.Parse("127.0.0.1"),2000)
let private loop(client:TcpClient,sr:StreamReader,sw:StreamWriter)=
async{
let autoEvent=new AutoResetEvent(false)
let timer=new Timer(a,autoEvent,0,2000)
autoEvent.WaitOne()|>ignore
}
let private startLoop()=
while true do
let client=listener.AcceptTcpClient()
let stream=client.GetStream()
let sr=new StreamReader(stream)
let sw=new StreamWriter(stream)
sw.AutoFlush<-true
Async.Start(loop(client,sr,sw))|>ignore
listener.Start()
startLoop()
listener.Stop()
定时器功能运行3次后不会退出。我想知道为什么?谢谢
我首先想提几件事,而不是使用Console.WriteLine("a")
,只需使用printfn "a"
。其次,您提供的代码片段不会终止,因此如果您在 FSI 中尝试,它会在主线程完成后继续运行。这在控制台应用程序中可能不是问题。要回答您的问题,它与异步工作流程有关。如果您喜欢这篇文章:Async Programming,您会注意到它们将异步计算作为子项生成,然后执行异步睡眠以让子项有机会开始。这与任务的调度方式有关。.NET Framework 使用“工作优先”策略。在阻塞事件迫使线程放弃当前任务之前,通常不会执行继续。这就是我让计时器事件运行的方式:
open System
open System.Threading
let mutable inc =0
let a(o:obj)=
let autoEvent=o :?> AutoResetEvent
printfn "a"
inc<-inc+1
if inc=3 then
printfn "hit 3!"
//autoEvent.Set()|>ignore
let private loop i =
async{
printfn "Started as child..."
let aWrap(o:obj) = // so that we can see which child prints
printfn "%d" i
let autoEvent=new AutoResetEvent(false)
let timer=new Timer(aWrap,autoEvent,0,2000)
autoEvent.WaitOne()|>ignore
}
let startLoopAsync() =
async {
let children =
[1..3]
|> List.map(fun i ->
Async.StartChild(loop i) // start as child
)
do! Async.Sleep 100 // give chance for children to start
children
|> List.iter (Async.RunSynchronously >> ignore) // wait for all children
}
startLoopAsync() |> (Async.RunSynchronously >> ignore) // wait for async loop start
Thread.Sleep(5000)
请注意,我使用了StartChild
. 我推荐这个是因为这里提到的事实:Async.Start vs. Async.StartChild。子异步任务不需要获得自己的取消令牌。相反,它从其父级继承。所以,如果我给 分配了一个取消标记startLoopAsync()
,我就可以取消那个任务,所有的孩子也会取消。最后,我建议保持一个句柄timer
,以防您需要停止再次发生的事件。不保留句柄将导致无法在不终止进程的情况下停止它。那是Thread.Sleep(5000)
为了什么。为了表明在异步任务完成后,计时器会不断触发事件,直到进程终止(如果您使用它进行测试,则需要终止 FSI)。
我希望这能回答你的问题,
干杯!
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句