这个程序工作正常:
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客户端时,我在异步块中放入相同的代码:
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()
计时器功能在运行三次时不会退出。我想知道原因吗?谢谢
答案 0 :(得分:1)
我首先想提一些事情,而不是使用Console.WriteLine("a")
,只需使用printfn "a"
即可。其次,您提供的代码片段不会终止,因此如果您在FSI中尝试它,它将在主线程完成后继续运行。这可能不是控制台应用程序中的问题。要回答您的问题,它与异步工作流程有关。如果你喜欢这篇文章:Async Programming,你会注意到它们会像孩子一样产生异步计算,然后执行异步睡眠以给孩子一个开始的机会。这与任务的安排方式有关。 .NET框架使用“先工作”策略。在阻塞事件迫使线程放弃当前任务之前,通常不会执行Continuations。这就是我运行计时器事件的方式:
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)。
我希望这能回答你的问题,
干杯!