在多线程code。使用F#事件 [英] Using F# Event in multi-threaded code

查看:137
本文介绍了在多线程code。使用F#事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的工作很多,在F#异步工作流程和代理商,同时我会一点点深入到活动中,我注意到,事件< _>()类型不是线程安全的。在这里我不是在谈论引发事件的通病。实际上,我在谈论订阅和删除/从事件处理。出于测试目的,我写了这个短节目

I'm working a lot with async workflows and agents in F#, while i was going a little bit deeper into Events i noticed that the Event<_>() type is not Thread-safe. Here i'm not talking about the common problem of raising an event. I'm actually talking about subscribing and removing/disposing from an event. For testing purpose i have written this short program

let event = Event<int>()
let sub   = event.Publish

[<EntryPoint>]
let main argv =
    let subscribe sub x = async {
        let mutable disposables = []
        for i=0 to x do
            let dis = Observable.subscribe (fun x -> printf "%d" x) sub
            disposables <- dis :: disposables
        for dis in disposables do
            dis.Dispose()
    }

    Async.RunSynchronously(async{
        let! x = Async.StartChild (subscribe sub 1000)
        let! y = Async.StartChild (subscribe sub 1000)
        do! x
        do! y
        event.Trigger 1
        do! Async.Sleep 2000
    })
    0

该计划很简单,我创建一个事件和订阅事件的具体数额到它的功能,之后每次处理的处理程序。我用另一异步计算产卵与Async.StartChild那些功能的两个实例。后两个功能完成我触发事件,看看是否有一些处理仍然离开了。

The program is simple, i create an event and a function that subscribes a specific amount of events to it, and after that dispose every handler. I use another async computation to spawn two instances of those function with Async.StartChild. After both functions finished i trigger the event to see if there are some handlers still left.

但是,当 event.Trigger(1)被称为结果是,仍有一些处理reigstered事件。由于一些1将被打印到控制台。这一般意味着,认购和/或处置不是线程安全的。

But when event.Trigger(1) is called the result is that there are still some handlers reigstered to the event. As some "1" will be printed to the console. That in general means that subscribing and/or Disposing is not thread-safe.

这是没有预期的我。如果订阅和处理是不是线程安全如何能够在一般的事件安全使用?肯定的事件也可以在线程以外使用,并触发不平行或在不同的线程派生的任何功能。但它是某种正常的,我认为事件在异步使用的,基于代理code或一般使用线程。它们经常被用来作为通讯收集Backroundworker线程的信息。与Async.AwaitEvent能够订阅事件。如果订阅和处理是不是线程安全的,怎么可能使用活动在这样的环境?和目的有Async.AwaitEvent?考虑到一个异步流程不会线程希望只使用Async.AwaitEvent基本上是设计打破如果订阅/配置到一个事件是不是线程安全的默认。

And that is what i didn't expected. If subscribing and Disposing is not thread-safe how can events in general safely be used? Sure events also can be used outside of threads, and a trigger don't spawn any function in parallel or on different threads. But it is somehow normal to me that Events are used in Async, Agent based code or in general with Threads. They are often used as a communication to gather information of Backroundworker Threads. With Async.AwaitEvent it is possible to subscribe to an event. If subscribing and Disposing is not thread-safe, how is it possible to use Events in such an environment? And which purpose has Async.AwaitEvent? Considering that an Async workflow does Thread hoping just using Async.AwaitEvent is basically "broken by design" if subscribing/disposing to an Event is not thread-safe by default.

我面对的一般问题。它是纠正注册和产权处置是不是线程安全的?从我的例子似乎看起来像它,但也许我错过了一些重要的细节。我目前使用的事件很多我的设计,我通常有MailboxProcessors和使用事件的通知。所以,问题是。如果事件不是线程安全我目前使用的不是线程安全可言了整个设计。那么,什么是造成这种局面的解决?创建一个全新的线程安全的事件实现?难道他们已经存在面对这个问题一些实现?还是有其他的选择,使用时安全的高度线程化的环境呢?

The general question i'm facing is. Is it correct that Subscribing and Disposing is not thread-safe? From my example it seems to look like it, but probably i missed some important detail. I currently use Event a lot in my design, i usually have MailboxProcessors and use events for notification. So the question is. If events are not thread-safe the whole design i'm currently using is not thread-safe at all. So what is an fix for this situation? Creating a whole new thread-safe event implementation? Do they already exists some implementations that face this problem? Or are there other options to use Event safely in a highly threaded environment?

推荐答案

仅供参考;为活动&LT的执行情况; INT&GT; 可以发现的这里

FYI; the implementation for Event<int> can be found here.

我们感兴趣的似乎是:

member e.AddHandler(d) =
  x.multicast <- (System.Delegate.Combine(x.multicast, d) :?> Handler<'T>)
member e.RemoveHandler(d) = 
  x.multicast <- (System.Delegate.Remove(x.multicast, d) :?> Handler<'T>)

订阅事件结合传递到订阅事件处理当前的事件处理。这种结合事件处理程序替换当前之一。

Subscribing to an event combines the current event handler with the event handler passed into subscribe. This combined event handler replaces the current one.

从并发的角度的问题是,我们在这里有一个竞争条件在并发用户可能使用来当前的事件处理与结合,最后一个写回处理赢(最后是一个难以理解的概念并发这些天,但NVM)。

The problem from a concurrency perspective is that here we have a race-condition in that concurrent subscribers might use the came current event handler to combine with and the "last" one that writes back the handler win (last is a difficult concept in concurrency these days but nvm).

有什么能在这里做的是通过引进 Interlocked.CompareAndExchange A CAS循环但补充说,伤害非并发用户的性能开销。这件事情,人们可以做PR关,虽然,看看它是否由F#社区看好。

What could be done here is to introduce a CAS loop using Interlocked.CompareAndExchange but that adds performance overhead that hurts non-concurrent users. It's something one could make a PR off though and see if it viewed favourably by the F# community.

WRT你做什么它的第二个问题我只能说我会做什么。我会去支持保护订阅/退订创建一个版本 FSharpEvent 的选项。也许它的基础 FSharpEvent 的如果你的公司FOSS政策允许它。如果事实证明是成功的话那可能构成未来公关F#核心libary。

WRT to your second question on what to do about it I can just say what I would do. I would go for the option of creating a version of FSharpEvent that supports protected subscribe/unsubscribe. Perhaps base it of FSharpEvent if your company FOSS policy allows it. If it turns out a success then it could form a future PR to F# core libary.

我不知道你的要求,但它也有可能是,如果你需要的是协同程序(即异步),而不是线程,然后有可能重写程序只使用1个线程,因此你不会受此影响竞争状态。

I don't know your requirements but it's also possible that if what you need is coroutines (ie Async) and not threads then it's possible to rewrite the program to use only 1 thread and thus you won't be affected by this race condition.

这篇关于在多线程code。使用F#事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆