gpt4 book ai didi

memory-leaks - F# 可观察事件是否消除、调解或与弱引用的需要无关?

转载 作者:行者123 更新时间:2023-12-04 14:23:57 25 4
gpt4 key购买 nike

由于 observables 通常是 IDisposable如果有的话,这将如何改变在事件处理程序中使用弱引用或任何其他基于事件的内存泄漏/GC 锁定引用的需要?

虽然我主要关注/需要的是 WPF,但我正在寻找更广泛的示例并试图了解我可能需要弱引用的位置。

F#的Observable.add没有提供解除事件的方法,所以我认为它不太可能成为泄漏的来源。
示例代码:

type Notifier() = 
let propChanged = new Event<_,_>()
member __.Foo() = ()
interface INotifyPropertyChanged with
[<CLIEvent>]
member __.PropertyChanged = propChanged.Publish
abstract member RaisePropertyChanged : string -> unit
default x.RaisePropertyChanged(propertyName : string) = propChanged.Trigger(x, PropertyChangedEventArgs(propertyName))


Notifier() :?> INotifyPropertyChanged
|> Observable.add(fun _ -> printfn "I'm hooked on you")

最佳答案

F#'s Observable.add doesn't provide a way to unhook the event, so I'm thinking it's less likely to be a source of leaks



其实恰恰相反。 Observable.add ,根据文档,永久订阅该事件,并强制“泄漏”。它有效地执行了无法取消订阅的事件处理程序添加。

一般来说,用 Observable (在 F# 和 C# 中),你应该倾向于使用 .subscribe ,并在完成后处理订阅句柄。

正如@rmunn 提到的, Gjallarhorn可以作为在某些场景中使用 observable 的替代方案(并根据需要与它们很好地集成)。在编写它时,我的主要目标之一是使订阅不会泄漏 - 所有订阅都使用基于弱引用的混合推/拉模型,这可以防止许多基于事件和可观察的泄漏问题代码。

为了演示,我使用可观察对象和 Gjallarhorn 的信号将您的代码的变体放在一起。如果您在调试器之外的发布版本中运行它,您将看到不同之处:
type Notifier() = 
let propChanged = new Event<_,_>()
member __.Foo() = ()
interface INotifyPropertyChanged with
[<CLIEvent>]
member __.PropertyChanged = propChanged.Publish
abstract member RaisePropertyChanged : string -> unit
default x.RaisePropertyChanged(propertyName : string) = propChanged.Trigger(x, PropertyChangedEventArgs(propertyName))

let obs () =
use mre = new ManualResetEvent(false)

let not = Notifier()

do
let inpc = not :> INotifyPropertyChanged
inpc.PropertyChanged
|> Observable.add (fun p -> printfn "Hit %s!" p.PropertyName)

async {
for i in [0 .. 10] do
do! Async.Sleep 100
printfn "Raising"
not.RaisePropertyChanged <| sprintf "%d" i
mre.Set () |> ignore
} |> Async.Start

printfn "Exiting block"

GC.Collect() // Force a collection, to "cleanup"
mre.WaitOne() |> ignore

let signals () =
use mre = new ManualResetEvent(false)

let not = Mutable.create 0

do
not
|> Signal.Subscription.create (fun v -> printfn "Hit %d!" v)
|> ignore // throw away subscription handle

async {
for i in [0 .. 10] do
do! Async.Sleep 100
printfn "Setting"
not.Value <- i
mre.Set () |> ignore
} |> Async.Start

printfn "Exiting block"

GC.Collect() // Force a collection, to "cleanup"
mre.WaitOne() |> ignore


[<STAThread>]
[<EntryPoint>]
let main _ =
printfn "Using observable"
obs ()

printfn "Using signals"
signals ()

1

请注意,两者都做类似的事情 - 他们创建一个“源”,然后在单独的范围内订阅它并丢弃一次性订阅句柄( Observable.add 只不过是 subscribe |> ignore - see code for details. )。在调试器之外的发布版本中运行时(调试器阻止清理发生),您会看到:
Using observable
Exiting block
Raising
Hit 0!
Raising
Hit 1!
Raising
Hit 2!
Raising
Hit 3!
Raising
Hit 4!
Raising
Hit 5!
Raising
Hit 6!
Raising
Hit 7!
Raising
Hit 8!
Raising
Hit 9!
Raising
Hit 10!
Using signals
Exiting block
Setting
Setting
Setting
Setting
Setting
Setting
Setting
Setting
Setting
Setting
Setting
Press any key to continue . . .

在可观察的情况下,调用 .add永久保存对通知程序的引用,防止它被垃圾收集。对于信号,信号订阅将 GC 并自动“取消挂接”,从而防止显示来自 Hit 的调用。

关于memory-leaks - F# 可观察事件是否消除、调解或与弱引用的需要无关?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42417616/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com