Multithreading F#事件的线程安全引发

Multithreading F#事件的线程安全引发,multithreading,events,f#,Multithreading,Events,F#,我正在尝试做一个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<_>) =

我正在尝试做一个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
type Worker()=
let locker=obj()
让computedValue=ref无
let start=ref false
let completed=Event()
让我们什么都不做(=)
成员x.Compute(回调:操作)=
让workAlreadyStarted,操作=
锁柜(乐趣()->
匹配!计算值与
|一些价值->
true,(fun()->callBack.Invoke值)
|无->
completed.Publish.Add callBack.Invoke
如果!从那时开始
没错,多诺辛
其他的
开始:=真
错误,不正确)
行动()
如果不工作,则重新启动
异步{
//计算结果繁重
让result=“result”
锁柜(乐趣()->
computedValue:=某个结果
已完成。触发结果)
}|>异步启动
但有一个问题,我想在锁之外触发已完成的事件,但我想确保触发是线程安全的(实际上,在这个小示例中,我可以在锁之外触发事件,因为我知道没有其他人会订阅它,但并不总是这样)

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

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

    void Raise()
    {
        Action<string> myEventCache;
        lock (locker)
        {
            myEventCache = MyEvent;
        }
        if (myEventCache != null)
        {
            myEventCache("result");
        }
    }
objectlocker=newobject();
事件行动事件;
作废
{
行为学;
锁(储物柜)
{
myEventCache=MyEvent;
}
如果(myEventCache!=null)
{
myEventCache(“结果”);
}
}

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

这在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)
let mutable completed=Event()
//...
设ev=Event()
让iev=ev.Publish
iev.Add(已完成.触发器)
iev.Add(callBack.Invoke)

已完成您确定C#会冻结订户列表吗?您不只是引用了事件吗?每当您向事件添加新的处理程序时,您都会创建一个新的委托值并替换以前的值,因此,当我复制委托值时,如果在此之后添加了另一个处理程序,它将不会链接到所创建的副本,如果您需要访问订阅服务器列表,然后,您可以使用自己的
IEvent
实现,而不是
Event.Publish提供的实现。