[英]Thread-safe raising of F# events
我正在嘗試進行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#事件執行等效操作,凍結鎖內的訂戶列表,但在鎖外調用它?
這在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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.