3

我正在尝试进行 F# 异步计算,该计算在准备好时调用 C# 回调。代码如下:

type Worker() = 

    let locker = obj()
    let computedValue = ref None
    let started = ref false
    let completed = Event<_>()

    let doNothing() = ()

    member x.Compute(callBack:Action<_>) = 
        let workAlreadyStarted, action = 
            lock locker (fun () -> 
                match !computedValue with
                | Some value ->
                    true, (fun () -> callBack.Invoke value)
                | None ->
                    completed.Publish.Add callBack.Invoke
                    if !started then                            
                        true, doNothing
                    else
                        started := true
                        false, doNothing)
        action()
        if not workAlreadyStartedthen
            async {                

                // heavy computation to calc result
                let result = "result"

                lock locker (fun () -> 
                    computedValue := Some result
                    completed.Trigger result)
            } |> Async.Start

但是有一个问题,我想在锁外触发完成的事件,但我想确保触发是线程安全的(实际上,在这个小例子中,我可以在锁外触发事件,因为我不知道其他人将订阅它,但并非总是如此)。

在 C# 事件中,这很容易实现:

    object locker = new object();
    event Action<string> MyEvent;

    void Raise()
    {
        Action<string> myEventCache;
        lock (locker)
        {
            myEventCache = MyEvent;
        }
        if (myEventCache != null)
        {
            myEventCache("result");
        }
    }

如何对 F# 事件执行等效操作,冻结锁内的订阅者列表但在锁外调用它?

4

1 回答 1

1

这在 F# 中并不那么简单,因为Event<_>不公开其订阅者列表,该列表由Add/突变Remove

您可以通过为每个处理程序创建一个新事件来避免这种突变。

let mutable completed = Event<_>()

//...

let ev = Event<_>()
let iev = ev.Publish
iev.Add(completed.Trigger)
iev.Add(callBack.Invoke)
completed <- ev

//...

let ev = lock locker <| fun () -> 
    computedValue := Some result
    completed
ev.Trigger(result)
于 2013-02-25T15:46:55.560 回答