我正在寻找一种无锁的方式来Async在F#中的两个s 之间发出信号.我有两个尾递归async函数,我想要一个产生,直到另一个信号发出,然后继续下一个递归.我可以使用一个事件,但看起来.NET事件在内部使用锁.到目前为止,我发现的唯一解决方案是使用来自ntdll.dll的键控事件,但我更喜欢不需要直接引用特定于平台的DLL的解决方案.有什么方法可以使用System.Threading.Interlocked或其他.NET技术来实现这一目标吗?
这是我想要实现的一个简单示例:
let rec loop1 () =
async {
// do work
// somehow signal loop2
return! loop1 ()
}
let rec loop2 state =
async {
// wait for signal from loop1
// do work
return! loop2 state // This would actually be a new state, not the old state
}
Run Code Online (Sandbox Code Playgroud)
我查看了 Szer 在 Hopac 的 IVar 上对事件进行建模的建议,并研究了标准 F# 事件是如何实现的。结合两者,我想出了这个:
open System
open System.Threading
type LockFreeEvent<'args>() =
let mutable multicast: Handler<'args> = null
let wait =
let spin = SpinWait()
spin.SpinOnce
member __.Trigger arg =
match multicast with
| null -> ()
| d -> d.Invoke(null, arg) |> ignore
member __.Publish =
{new IEvent<'args> with
member __.AddHandler handler =
let snapshot = multicast
while snapshot <> Interlocked.CompareExchange<Handler<'args>>(&multicast, Delegate.Combine(multicast, handler) :?> Handler<'args>, snapshot) do
wait ()
member __.RemoveHandler handler =
let snapshot = multicast
while snapshot <> Interlocked.CompareExchange(&multicast, Delegate.Remove(multicast, handler) :?> Handler<'args>, snapshot) do
wait ()
member this.Subscribe observer =
let handler = new Handler<_>(fun sender args -> observer.OnNext(args))
(this :?> IEvent<_,_>).AddHandler(handler)
{ new IDisposable with
member __.Dispose() = (this :?> IEvent<_,_>).RemoveHandler(handler)
}
}
Run Code Online (Sandbox Code Playgroud)
这看起来怎么样?我认为这应该实现与标准 F# 事件相同的功能,但没有锁定,除非Delegate.Combine. Trigger我想我可能也需要采取不同的做法。