1

我想使用 BeginGetResponse 方法来调用我在列表中保存的许多 URL。我有两个关于如何实施的问题:

  1. 根据http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetresponse(v=vs.95).aspx中的示例

我们用:

public static ManualResetEvent allDone= new ManualResetEvent(false);

因为它与其他线程共享,所以在 Web 应用程序中使用静态成员是否明智?这会导致问题吗?

  1. 我怎么知道所有回调何时完成?我需要对结果做一个总结报告

谢谢

4

3 回答 3

3

虽然您可以使用事件,但我建议在Task<T>上使用和FromAsync方法,如下所示:TaskFactory

// Execution of tasks starts here because of the
// call to ToArray.
Task<WebResponse>[] tasks = uris.Select(u => {
    // Create the request.
    WebRequest req = ...;

    // Make the call to return the response asynchronously with
    // a Task.
    return Task.Factory.FromAsync(req.BeginGetResponse,
        req.EndGetResponse, null);
}).ToArray();

一旦你有了它,你可以使用类上的方法Task<T>轻松地等待所有实例,如下所示:ContinueWhenAllTaskFactory

Task.Factory.ContinueWhenAll(tasks, t => {
     // Note that t is an array of Task, so you have to cast 
     // each element to a Task<WebRequest>.
     // Process all of them here.
});

请注意,上面的返回 aTask完成后您将不得不等待或继续(如果您担心通知)。

如果您使用的是 .NET 4.5,则不需要使用类ContinueWhenAll上的方法TaskFactory,但可以使用类上的WhenAll方法Task来执行工作:

// Note that work does not start here yet because of deferred execution.
// If you want it to start here, you can call ToArray like above.
IEnumerable<Task<WebResponse>> tasks = uris.Select(u => {
    // Create the request.
    WebRequest req = ...;

    // Make the call to return the response asynchronously with
    // a Task.
    return Task.Factory.FromAsync(req.BeginGetResponse,
        req.EndGetResponse, null);
});

// Execution will start at this call:
Task<Task<WebRequest>[]> allTasks = Task.WhenAll(tasks);

// Continue or wait here.

请注意,上面的内容是在显示正在使用 .NET 3.5之前。

于 2012-07-10T19:45:15.357 回答
1

我猜你正在尝试做这样的事情:

int total = urls.Count;
ManualResetEvent evt = new ManualResetEvent();
ConcurrentBag<WebResponses> responses = new ConcurrentBag<WebResponse>();

foreach(Uri in uri)
{
    HttpWebRequest req = ...;
    req.BeginGetResponse(res=>
    {
        WebResponse res = req.EndGetResponse();

        // do what you need with the response.
        // maybe add it to a collection so you can report on it later:
        responses.Add(res);

        if(Interlocked.Decrement(ref total) == 0)
        {
            // this was the last response. set event.
            evt.Set();
        }
    }, null);
}

evt.Wait();

foreach(WebResponse res in responses)
{
    // report something about the response.
}

请注意,最佳工作流程不需要事件。为了获得额外的功劳,请一起摆脱它并将您的最终逻辑移动到设置事件的 if 中。

此外,此代码未经测试且缺乏错误处理,因此如果您使用它,请务必添加它。

于 2012-07-10T19:30:31.197 回答
1
  1. 如果你想在主线程中等待完成,那么这个解决方案不是很好。第一个请求会将事件的状态更改为“设置”。因此,主线程将在第一个请求完成后继续执行。
  2. 建议您使用CountdownEvent

       using(var countdownEvent = new CountdownEvent(list.Count))
       {
           // launch requests with countdownEvent.Signal(); in the end
           countdownEvent.Wait();
       }
    

    您必须在 RequestState 中存储对 countdownEvent 的引用。另外,不要忘记控制超时 - 使用ThreadPool.RegisterWaitForSingleObject.

于 2012-07-10T19:56:19.687 回答