首先,我已经阅读了类似的问题,并没有给我一个连贯的解释。我使用BlockingCollection<WebClient> ClientQueue
来提供Webclients。我给他们一个处理函数并启动异步抓取:
// Create queue of WebClient instances
BlockingCollection<WebClient> ClientQueue = new BlockingCollection<WebClient>();
for (int i = 0; i < 10; i++)
{
ClientQueue.Add(new WebClient());
}
//Triggering Async Calls
foreach (var item in source)
{
var worker = ClientQueue.Take();
worker.DownloadStringCompleted += (sender, e) => HandleJson(sender, e, ClientQueue, item);
worker.DownloadStringAsync(uri);
}
public static void HandleJson(object sender, EventArgs e, BlockingCollection<WebClient> ClientQueue, string item)
{
var res = (DownloadStringCompletedEventArgs) e;
var jsonData = res.Result;
var worker = (WebClient) sender;
var root = JsonConvert.DeserializeObject<RootObject>(jsonData);
// Record the data
while (worker.IsBusy) Thread.Sleep(5); // wait for the webClient to be free
ClientQueue.Add(worker);
}
我收到此错误消息:
WebClient不支持并发I / O操作。
其他主题:
这里的答案建议问题是等到WebClient.IsBusy = false
,但我在将webclient放回队列之前这样做了。我不明白为什么客户在自己做出IsBusy=false
之后无法执行新请求
https://stackoverflow.com/a/9765812/7111121
这里建议使用回收网络客户端来优化流程 https://stackoverflow.com/a/7474959/2132352
这里建议实现一个新的WebClient(当然是简单的解决方案,但我不想隐藏所用对象的工作方式)。它还建议取消操作,但这没有帮助。
答案 0 :(得分:1)
问题是,每次从队列中获取特定的WebClient时,都会为worker.DownloadStringCompleted
事件注册新的事件处理程序而不取消注册以前的事件处理程序 - 因此事件处理程序会累积。因此,异步下载完成后会多次调用HandleJson
,因此ClientQueue.Add(worker)
也会将同一个客户端多次返回到队列。然后,在同一WebClient上发布两个并发下载只是时间问题。
通过在创建WebClient期间只注册一次事件处理程序,并从item
方法中删除HandleJson
参数,可以轻松修复此问题。
BlockingCollection<WebClient> ClientQueue = new BlockingCollection<WebClient>();
for (int i = 0; i < 2; i++)
{
var worker = new WebClient();
worker.DownloadStringCompleted += (sender, e) => HandleJson(sender, e, ClientQueue);
ClientQueue.Add(worker);
}
如果需要参数item
,请将其作为参数传递给DownloadStringAsync(uri, item)
并从res.UserState
读取:
foreach (var item in source)
{
var worker = ClientQueue.Take();
worker.DownloadStringAsync(uri, item);
}
public static void HandleJson(object sender, DownloadStringCompletedEventArgs e, BlockingCollection<WebClient> ClientQueue)
{
string item = (string)res.UserState;
...
}