Asyncs之间的无锁信号方式

Aar*_*ach 9 .net f# lock-free

我正在寻找一种无锁的方式来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)

Aar*_*ach 4

我查看了 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我想我可能也需要采取不同的做法。

  • 为什么不改用Hopac?当我上次使用异步时,它并不是真正用于 CPU 密集型计算(其中锁会搞乱事情)。对此,Hopac 是更好的选择。异步非常适合 IO 密集型计算。 (2认同)