此代码在mono上运行(5.4.1.7(。
我正在使用F#的代理来处理我的Web应用程序中的大量数据处理,并且其中一条消息被关闭。处理发布的关闭消息时,代理会清理某些东西并停止其消息循环。这可以正常工作且花花公子,但是如果我尝试执行Finalize()
的关闭,我的脸上会炸毁。我已经设法复制了以下内容:
open System
open System.Threading
type ConsoleMessage =
| Clear
| Println of string
// Reply back (with unit) so that calling code is able to wait for the agent to clean up (for code dependent on the
// agent's resources definitely being released and such)
| Shutdown of AsyncReplyChannel<unit>
type ConsoleAgent() =
let mutable disposed = false
let mutable stopped = false
let agent = MailboxProcessor.Start(fun agent ->
let rec messageLoop () = async {
let! message = agent.Receive ()
match message with
| Clear -> System.Console.Clear ()
| Println str -> printfn "%s" str
| Shutdown rc ->
// Cleanup goes here
printfn "Shutting Down"
stopped <- true
rc.Reply ()
System.Threading.Thread.Sleep 100
if not stopped then
return! messageLoop () }
messageLoop ())
member this.Post msg = agent.Post msg
member this.PostAndAsyncReply msg = agent.PostAndAsyncReply msg
member this.Dispose disposing =
printfn "Disposing (disposing = %b)" disposing
if not disposed then
Async.RunSynchronously (agent.PostAndAsyncReply Shutdown)
disposed <- true
override this.Finalize () =
this.Dispose false
interface IDisposable with
member this.Dispose () =
this.Dispose true
module Main =
[<EntryPoint>]
let main args =
let console = new ConsoleAgent()
console.Post (Println "Print 1")
console.Post (Println "Print 2")
Thread.Sleep 1000
0
当然,在实际应用中,他们与控制台打印无关。
这是我得到的堆叠:
Unhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object
at System.Runtime.Remoting.Contexts.SynchronizationAttribute.EnterContext () [0x00000] in /Users/builder/data/lanes/4992/mono-mac-sdk/external/bockbuild/builds/mono-x64/mcs/class/corlib/System.Runtime.Remoting.Contexts/SynchronizationAttribute.cs:184
at System.Threading.WaitHandle.WaitOneNative (System.Runtime.InteropServices.SafeHandle waitableSafeHandle, System.UInt32 millisecondsTimeout, System.Boolean hasThreadAffinity, System.Boolean exitContext) [0x0002d] in /Users/builder/data/lanes/4992/mono-mac-sdk/external/bockbuild/builds/mono-x64/mcs/class/corlib/System.Threading/WaitHandle.cs:111
at System.Threading.WaitHandle.InternalWaitOne (System.Runtime.InteropServices.SafeHandle waitableSafeHandle, System.Int64 millisecondsTimeout, System.Boolean hasThreadAffinity, System.Boolean exitContext) [0x00014] in /Users/builder/data/lanes/4992/mono-mac-sdk/external/bockbuild/builds/mono-x64/mcs/class/referencesource/mscorlib/system/threading/waithandle.cs:250
at System.Threading.WaitHandle.WaitOne (System.Int64 timeout, System.Boolean exitContext) [0x00000] in /Users/builder/data/lanes/4992/mono-mac-sdk/external/bockbuild/builds/mono-x64/mcs/class/referencesource/mscorlib/system/threading/waithandle.cs:239
at System.Threading.WaitHandle.WaitOne (System.Int32 millisecondsTimeout, System.Boolean exitContext) [0x00019] in /Users/builder/data/lanes/4992/mono-mac-sdk/external/bockbuild/builds/mono-x64/mcs/class/referencesource/mscorlib/system/threading/waithandle.cs:206
at Microsoft.FSharp.Control.AsyncImpl+ResultCell`1[T].TryWaitForResultSynchronously (Microsoft.FSharp.Core.FSharpOption`1[T] timeout) [0x0002a] in <5a7d678a904cf4daa74503838a677d5a>:0
at Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronouslyInCurrentThread[a] (System.Threading.CancellationToken token, Microsoft.FSharp.Control.FSharpAsync`1[T] computation) [0x0001c] in <5a7d678a904cf4daa74503838a677d5a>:0
at Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously[a] (System.Threading.CancellationToken token, Microsoft.FSharp.Control.FSharpAsync`1[T] computation, Microsoft.FSharp.Core.FSharpOption`1[T] timeout) [0x00013] in <5a7d678a904cf4daa74503838a677d5a>:0
at Microsoft.FSharp.Control.FSharpAsync.RunSynchronously[T] (Microsoft.FSharp.Control.FSharpAsync`1[T] computation, Microsoft.FSharp.Core.FSharpOption`1[T] timeout, Microsoft.FSharp.Core.FSharpOption`1[T] cancellationToken) [0x00070] in <5a7d678a904cf4daa74503838a677d5a>:0
at Program+ConsoleAgent.Dispose (System.Boolean disposing) [0x00027] in /Users/jwostenberg/Code/FSharp/Sandbox/Sandbox/Program.fs:38
at Program+ConsoleAgent.Finalize () [0x00000] in /Users/jwostenberg/Code/FSharp/Sandbox/Sandbox/Program.fs:42
更重要的是,如果对象通过处置模式正确处理(例如将let console = new ConsoleAgent()
更改为use console = new ConsoleAgent()
(,则不会发生这种情况。我不能在不向后弯曲的情况下真正在自己的代码中做到这一点无论如何,垃圾收集器?
这是我的错,F#的错还是单声道的错?目前,我在尝试/捕获的尝试/捕获中将dispose((方法的相关部分包裹起来,但它确实很肮脏。
该方法处置的参数"处置"是有原因的。它区分了托管处置的托管应用程序和不受管理的应用程序。简而言之,处置(true(表示此呼叫是明确的(使用语句或F#的use
(。基本上是"正常" .NET编程的延续。
处置(false(意味着最终确定正在发生。这意味着任何引用的.NET对象都可以活着,处置或最终确定。因此,您的代码需要仅关心未管理的资源,并且不尝试以任何其他方式托管对象致电或使用。
重要的是,dispose((在最终确定器时不会自动调用。使示例正确需要两个更改:
- 明确控制一次性对象的状态
- 仅在处置对象时发送消息,而不是最终确定
代码:
member this.Dispose disposing =
if disposing && not disposed then
Async.RunSynchronously (agent.PostAndAsyncReply Shutdown)
disposed <- true
module Main =
[<EntryPoint>]
let main args =
use console = new ConsoleAgent()
Thread.Sleep 1000
0
您几乎需要覆盖Finalize
的场景,而且看起来不像使用用例。请参阅此处和整篇文章的"实施者的注释"部分。
Object.Finalize
方法默认没有任何作用,但是您只应在必要时覆盖最终确定,并且仅发布非管理资源。
re:您的评论:
如何确保没有
Finalize
的MailboxProcessor
消息循环被关闭?
您可以只使用IDisposable
模式,也可以更明确地管理MailboxProcessor
S的寿命,这可能需要重构您的项目。
我不能在不向后弯曲的情况下真正在自己的代码中做到这一点他们无论如何都会通过垃圾收集器处置?
是的,您应该能够让他们"自然"地假设他们没有不受管理的资源。很难不看到现实世界的用例,但听起来您想对处理器的生命进行更多控制。这可能是一个XY问题。