F# 程序在 fsi 中运行正常,但作为 exe 挂起

F# program runs correctly in fsi, but hangs as an exe

我有一段代码可以在 MailboxProcessor 收到消息时向数据库中添加一行。它在 fsi 中 运行 时正常工作,但在编译为 exe 时挂起。脚本如下:

#r "../packages/Newtonsoft.Json/lib/net40/Newtonsoft.Json.dll"
#r "../packages/SQLProvider/lib/FSharp.Data.SqlProvider.dll"

open Newtonsoft.Json
open FSharp.Data.Sql
open System

let [<Literal>] ResolutionPath = __SOURCE_DIRECTORY__ + "/../build/" 
let [<Literal>] ConnectionString = "Data Source=" + __SOURCE_DIRECTORY__ + @"/test.db;Version=3"

// test.db is initialized as follows:
//
// BEGIN TRANSACTION;
//    CREATE TABLE "Events" (
//        `id`INTEGER PRIMARY KEY AUTOINCREMENT,
//        `timestamp` DATETIME NOT NULL
//    );
//    COMMIT;

type Sql = SqlDataProvider< 
            ConnectionString = ConnectionString,
            DatabaseVendor = Common.DatabaseProviderTypes.SQLITE,
            ResolutionPath = ResolutionPath,
            IndividualsAmount = 1000,
            UseOptionTypes = true >
let ctx = Sql.GetDataContext()

let agent = MailboxProcessor.Start(fun (inbox:MailboxProcessor<String>) ->  
    let rec loop() =
        async {
            let! msg = inbox.Receive()
            match msg with
            | _ ->
              let row = ctx.Main.Events.Create()
              row.Timestamp <- DateTime.Now
              printfn "Submitting"
              ctx.SubmitUpdates()
              printfn "Submitted"
            return! loop() 
        }
    loop() 
)

agent.Post "Hello"

编译为 exe 时,会打印 "Submitting",但随后会挂起。如果你想尝试一下,完整代码在 github here

问题似乎是主线程在 MailboxProcessor 可以处理它的邮箱之前退出。 FSI 是长期存在的,所以这并没有发生在那里。我改变了:

[<EntryPoint>]
let main argv =
    agent.Post "Hello"
    agent.Post "Hello again"
    0

[<EntryPoint>]
let main argv =
    agent.Post "Hello"
    agent.Post "Hello again"
    let waitLoop = async {
        while agent.CurrentQueueLength > 0 do
            printfn "Sleeping"
            do! Async.Sleep 1000
        }
    Async.RunSynchronously waitLoop
    0

现在代码按照我的预期执行了。