Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jsf-2/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asynchronous MailboxProcessor第一个循环可以';如果程序立即失败,则不能运行_Asynchronous_F#_Mailboxprocessor - Fatal编程技术网

Asynchronous MailboxProcessor第一个循环可以';如果程序立即失败,则不能运行

Asynchronous MailboxProcessor第一个循环可以';如果程序立即失败,则不能运行,asynchronous,f#,mailboxprocessor,Asynchronous,F#,Mailboxprocessor,我有一个命令定期运行SFTP检查并将结果记录到文件中 let logPath = Path.Combine(config.["SharedFolder"],timestamp) let sw = new StreamWriter(logPath,true) //... [<EntryPoint>] let main argv = try sftpExample config.["SharedFolder"] config.["SFTPFolder"] 22

我有一个命令定期运行SFTP检查并将结果记录到文件中

let logPath = Path.Combine(config.["SharedFolder"],timestamp)
let sw = new StreamWriter(logPath,true)
//...
[<EntryPoint>]
let main argv = 
    try 
        sftpExample config.["SharedFolder"] config.["SFTPFolder"] 22 "usr" "pswd" |> ignore
    with 
    | ex -> 
        ex.Message |> printerAgent.Post
        printfn "%s" ex.Message // <- NOTICE THIS LINE
    sw.Close()
    sw.Dispose()
0  
调用它将消息写入日志

let sftpExample local host port username (password:string) =
    async {
        use client = new SftpClient(host, port, username, password)
        client.Connect()
        sprintf "Connected to %s\nroot dir list" host  |> printerAgent.Post
        do! downloadDir local client ""   
        sprintf "Done, disconnecting now" |> printerAgent.Post
        client.Disconnect()
    } |> Async.RunSynchronously
文件下载是异步的,以及相应的消息,但似乎都可以正常工作

问题在于-如果由于某些原因,sftp连接立即失败,
MailboxProcessor
没有时间记录异常消息

我试图做的是在结尾前添加一个
printfn“%s”ex.Message
:我只是想知道是否有人设想了一个更好的解决方案


仅供参考,完整的代码已存在。

事实上,您希望程序等待,直到MailboxProcessor完成其所有消息队列的处理后,程序才会退出。您的
printfn“%s”ex.Message
似乎正在工作,但它不能保证工作:如果MailboxProcessor的队列中有多个项目,则运行
printfn
函数的线程可能在MailboxProcessor的线程有时间浏览其所有消息之前完成

我建议的设计是将
printerAgent
的输入更改为DU,如下所示:

type printerAgentMsg =
    | Message of string
    | Shutdown
然后,当您希望printer agent完成其消息的发送时,使用
main
功能中的(并在文档中记录用法示例),然后向其发送
Shutdown
消息。请记住,MailboxProcessor消息是排队的:当它收到
Shutdown
消息时,它将已经处理了队列中的其余消息。因此,处理
Shutdown
消息所需要做的就是返回一个
单元
应答,而不再调用它的循环。由于您使用的是
PostAndReply
而不是
PostAndReplyAsync
,因此在MailboxProcessor完成所有工作之前,主功能将一直阻塞。(为了避免永远阻塞,我建议在
PostAndReply
调用中设置一个10秒的超时;默认超时为-1,表示永远等待)

编辑:以下是我的意思的一个例子(未经测试,使用风险自负):

type printerAgentMsg =
    | Message of string
    | Shutdown of AsyncReplyChannel<unit>

let printerAgent = MailboxProcessor.Start(fun inbox-> 
    // the message processing function
    let rec messageLoop() = async{        
        // read a message
        let! msg = inbox.Receive()
        // process a message
        match msg with
        | Message text ->
            sw.WriteLine("{0}: {1}", DateTime.UtcNow.ToShortTimeString(), text)
            printfn "%s" text
            // loop to top
            return! messageLoop()
        | Shutdown replyChannel ->
            replyChannel.Reply()
            // We do NOT do return! messageLoop() here
        }
    // start the loop 
    messageLoop() 
    )

let logPath = Path.Combine(config.["SharedFolder"],timestamp)
let sw = new StreamWriter(logPath,true)
//...
[<EntryPoint>]
let main argv = 
    try 
        sftpExample config.["SharedFolder"] config.["SFTPFolder"] 22 "usr" "pswd" |> ignore
    with 
    | ex -> 
        ex.Message |> Message |> printerAgent.Post
        printfn "%s" ex.Message // <- NOTICE THIS LINE
    printerAgent.PostAndReply( (fun replyChannel -> Shutdown replyChannel), 10000)  // Timeout = 10000 ms = 10 seconds
    sw.Close()
    sw.Dispose()
键入printerAgentMsg=
|字符串消息
|关闭AsyncReplyChannel
让printerAgent=MailboxProcessor.Start(有趣的收件箱->
//消息处理功能
让rec messageLoop()=异步{
//读一条消息
let!msg=inbox.Receive()
//处理消息
配味精
|消息文本->
WriteLine(“{0}:{1}”,DateTime.UtcNow.ToShortTimeString(),text)
printfn“%s”文本
//循环到顶部
return!messageLoop()
|关闭回复通道->
replyChannel.Reply()
//这里不返回!messageLoop()
}
//开始循环
messageLoop()
)
让logPath=Path.Combine(配置。[“SharedFolder”],时间戳)
设sw=新StreamWriter(logPath,true)
//...
[]
让主argv=
尝试
sftpExample配置。[“SharedFolder”]配置。[“SFTPFolder”]22“usr”“pswd”|>忽略
具有
|ex->
例如Message |>Message |>printerAgent.Post
printfn“%s”例如消息//关机回复通道),10000)//超时=10000毫秒=10秒
sw.Close()
sw.Dispose()

最简单的解决方案是使用普通(同步)功能进行日志记录,而不是使用MailboxProcessor,或者在主功能的末尾使用一些日志框架和刷新日志记录程序。如果要继续使用
printingAgent
,可以实现如下“同步”模式:

type Msg =
    | Log of string
    | LogAndWait of string * AsyncReplyChannel<unit>

let printerAgent = MailboxProcessor.Start(fun inbox -> 
    let processLogMessage logMessage =
        sw.WriteLine("{0}: {1}", DateTime.UtcNow.ToShortTimeString(), logMessage)
        printfn "%s" logMessage
    let rec messageLoop() = async{        
        let! msg = inbox.Receive()
        match msg with 
        | Log logMessage ->
            processLogMessage logMessage
        | LogAndWait (logMessage, replyChannel) ->
            processLogMessage logMessage
            replyChannel.Reply()
        return! messageLoop()  
        }
    messageLoop() 
    )
或者同步地

printerAgent.Post(Log "Message")
printerAgent.PostAndReply(fun channel -> LogAndWait("Message", channel))
在主函数中记录异常时,应使用同步替代方法

printerAgent.PostAndReply(fun channel -> LogAndWait("Message", channel))