对于更广泛的上下文,此处为 my code ,可下载网址列表。
在我看来,使用use! response = request.AsyncGetResponse()
样式的URL提取时,没有好办法处理F#中的超时。我几乎所有工作都按照我的喜好(错误处理和异步请求和响应下载)保存了网站需要很长时间才能响应的问题。我当前的代码只是无限期挂起。我已经在我编写的等待300秒的PHP脚本上尝试过它。它一直等待着。
我找到了两种“解决方案”,这两种解决方案都是不可取的。
AwaitIAsyncResult
+ BeginGetResponse
与this other Stack Overflow question上 ildjarn 的答案相同。这样做的问题是,如果您排队了许多异步请求,则会在AwaitIAsyncResult
上人为阻止某些异步请求。换句话说,已经进行了发出请求的调用,但幕后的一些事情阻止了调用。这会导致在发出许多并发请求时过早触发AwaitIAsyncResult
上的超时。我的猜测是对单个域的请求数量的限制,或者只是对总请求的限制。
为了支持我的怀疑,我写了一些WPF应用程序来绘制请求似乎开始和结束的时间表。在我上面链接的代码中,注意计时器的启动和停止在第49和54行(调用第10行)。这是resulting timeline image。
当我将计时器开始移动到初始响应之后(所以我只计算下载内容的时间),the timeline looks a lot more realistic。注意,这是两个单独的运行,但除了启动计时器之外没有代码更改。我没有在startTime
之前直接测量use! response = request.AsyncGetResponse()
,而是在之后直接测量。
为了进一步支持我的主张,我制作了Fiddler2的时间表。这是the resulting timeline。 很明显,当我告诉他们时,请求并没有完全开始。
新帖子中的GetResponseStream
换句话说,同步请求和下载调用是在辅助线程中进行的。此 工作,因为GetResponseStream
尊重Timeout
对象上的WebRequest
属性。但是在这个过程中,我们失去了所有的等待时间,因为请求已经在线上并且响应还没有回来。我们不妨用C#...;)
Semaphore(5, 5)
或类似的东西吗?如果您有任何疑惑,请告诉我。
答案 0 :(得分:0)
AsyncGetResponse只是忽略发布的任何超时值...这是我们刚刚解决的解决方案:
open System
open System.IO
open System.Net
type Request = Request of WebRequest * AsyncReplyChannel<WebResponse>
let requestAgent =
MailboxProcessor.Start <| fun inbox -> async {
while true do
let! (Request (req, port)) = inbox.Receive ()
async {
try
let! resp = req.AsyncGetResponse ()
port.Reply resp
with
| ex -> sprintf "Exception in child %s\n%s" (ex.GetType().Name) ex.Message |> Console.WriteLine
} |> Async.Start
}
let getHTML url =
async {
try
let req = "http://" + url |> WebRequest.Create
try
use! resp = requestAgent.PostAndAsyncReply ((fun chan -> Request (req, chan)), 1000)
use str = resp.GetResponseStream ()
use rdr = new StreamReader (str)
return Some <| rdr.ReadToEnd ()
with
| :? System.TimeoutException ->
req.Abort()
Console.WriteLine "RequestAgent call timed out"
return None
with
| ex ->
sprintf "Exception in request %s\n\n%s" (ex.GetType().Name) ex.Message |> Console.WriteLine
return None
} |> Async.RunSynchronously;;
getHTML "www.grogogle.com"
即。我们委托给另一个代理并调用它提供异步超时...如果我们在指定的时间内没有得到代理的回复,我们就会中止请求并继续。
答案 1 :(得分:0)
我看到我的其他答案可能无法回答您的特定问题...这是任务限制器的另一个实现,不需要使用信号量。
open System
type IParallelLimiter =
abstract GetToken : unit -> Async<IDisposable>
type Message=
| GetToken of AsyncReplyChannel<IDisposable>
| Release
let start count =
let agent =
MailboxProcessor.Start(fun inbox ->
let newToken () =
{ new IDisposable with
member x.Dispose () = inbox.Post Release }
let rec loop n = async {
let! msg = inbox.Scan <| function
| GetToken _ when n = 0 -> None
| msg -> async.Return msg |> Some
return!
match msg with
| Release ->
loop (n + 1)
| GetToken port ->
port.Reply <| newToken ()
loop (n - 1)
}
loop count)
{ new IParallelLimiter with
member x.GetToken () =
agent.PostAndAsyncReply GetToken}
let limiter = start 100;;
for _ in 0..1000 do
async {
use! token = limiter.GetToken ()
Console.WriteLine "Sleeping..."
do! Async.Sleep 3000
Console.WriteLine "Releasing..."
} |> Async.Start