F#中的"限制"异步下载

Ben*_*jol 7 f# asynchronous timeout

我正在尝试下载从我博客的xml备份中引用的3000多张照片.我遇到的问题是,如果其中一张照片不再可用,整个异步会被阻止,因为AsyncGetResponse不会超时.

ildjarn帮助我整理了一个版本的AsyncGetResponse,它在超时时失败了,但是使用它会产生更多的超时 - 就好像刚排队的请求超时一样.似乎所有的WebRequests都是"立即"启动的,唯一让它工作的方法是将超时设置为下载所有这些组合所需的时间:这不是很好,因为这意味着我已经调整了超时,具体取决于图像的数量.

我达到了香草的极限async吗?我应该考虑反应性扩展吗?

这有点令人尴尬,因为我已经在这个特定的代码中问了两个 问题,而我仍然没有按照我想要的方式工作!

Tom*_*cek 9

我认为必须有一种更好的方法来发现文件不可用而不是使用超时.我不完全确定,但如果找不到文件,是否有某种方法可以使它抛出异常?然后你可以将async代码包装在里面try .. with,你应该避免大多数问题.

无论如何,如果你想编写自己的"并发管理器"并行运行一定数量的请求并将剩余的待处理请求排队,那么F#中最简单的选择就是使用代理(MailboxProcessor类型).以下对象封装了行为:

type ThrottlingAgentMessage = 
  | Completed
  | Work of Async<unit>

/// Represents an agent that runs operations in concurrently. When the number
/// of concurrent operations exceeds 'limit', they are queued and processed later
type ThrottlingAgent(limit) = 
  let agent = MailboxProcessor.Start(fun agent -> 
    /// Represents a state when the agent is blocked
    let rec waiting () = 
      // Use 'Scan' to wait for completion of some work
      agent.Scan(function
        | Completed -> Some(working (limit - 1))
        | _ -> None)
    /// Represents a state when the agent is working
    and working count = async { 
      while true do
        // Receive any message 
        let! msg = agent.Receive()
        match msg with 
        | Completed -> 
            // Decrement the counter of work items
            return! working (count - 1)
        | Work work ->
            // Start the work item & continue in blocked/working state
            async { try do! work 
                    finally agent.Post(Completed) }
            |> Async.Start
            if count < limit then return! working (count + 1)
            else return! waiting () }
    working 0)      

  /// Queue the specified asynchronous workflow for processing
  member x.DoWork(work) = agent.Post(Work work)
Run Code Online (Sandbox Code Playgroud)


Bri*_*ian 6

没有比这更容易了.:)

我认为你遇到的问题是问题领域的固有问题(而不仅仅是异步编程模型的问题,尽管它们确实在某种程度上相互作用).

假设您要下载3000张图片.首先,在您的.NET进程中,有一些类似于System.Net.ConnectionLimit或者我忘记名称的东西,例如会限制.NET进程可以同时运行的同时HTTP连接的数量(默认值只是'2 ' 我认为).因此,您可以找到该控件并将其设置为更高的数字,这将有所帮助.

但接下来,您的机器和互联网连接的带宽有限.因此,即使您可以尝试同时启动3000个HTTP连接,每个单独的连接也会因带宽管道限制而变慢.所以这也会与超时相互作用.(这甚至不考虑服务器上有哪种类型的限制/限制.也许如果你发送3000个请求,它会认为你是DoS攻击并将你的IP列入黑名单.)

因此,这是一个真正的问题域,其中良好的解决方案需要一些智能限制和流量控制,以便管理底层系统资源的使用方式.

正如在另一个答案中,F#代理(MailboxProcessors)是一个很好的编程模型,用于创作这样的限制/流控制逻辑.

(即使如此,如果大多数图片文件都像1MB,但是那里混合了1GB文件,那么单个文件可能会超时.)

无论如何,这不是问题的答案,只是指出问题领域本身存在多少内在的复杂性.(也许它也暗示了为什么UI'下载管理器'如此受欢迎.)