.net - MailboxProcessor 在 Finalize 期间崩溃

标签 .net asynchronous f# mono mailboxprocessor

此代码在 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# 的错还是 Mono 的错?目前,我已将 Dispose() 方法的相关部分包装在仅记录异常的 try/catch 中,但这感觉真的很脏。

最佳答案

Dispose 方法的参数“diswriting”在这里是有原因的。它区分 Dispose 的托管应用程序和非托管应用程序。简而言之,Dispose(true) 意味着此调用是显式的(using 语句或 F# 的 use)。它基本上是“正常”.NET 编程的延续。

Dispose(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

关于.net - MailboxProcessor 在 Finalize 期间崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48976175/

相关文章:

f# - 为什么要在自定义计算表达式生成器中使用Builder.Source()?

f# - 如何检查列表是否包含具有类型的可区分联合案例?

c# - 将通过 HTTP 上传到 ASP.NET 的文件进一步上传到 C# 和 SSH.NET 中的 SFTP 服务器

perl - 如何使用任何事件进行异步 www-mechanize

c# - 保护区与在基类中使用私有(private)区域

http - Web 服务器 - 如何解析请求?异步流标记器?

javascript - 在随机超时完成多个函数后,如何运行语句?

f# - 为什么在使用绑定(bind)到 `let` 的联合类型成员时需要括号?

c# - 设置 TextBlock 以在开头和结尾保留空白?

c# - XmlDocument读取XML文档注释问题