我有一个帐户列表。我想登录网站上的所有帐户。我想使用Parallel.ForEach
来处理所有帐户。
这就是我的代码:
Parallel.ForEach(Accounts,
acc =>
{
acc.WebProxy = null;
acc.Channel = "pelvicpaladin__";
Debug.WriteLine("Connecting to {0}.", new object[] { acc.Username });
acc.ConnectToIrc().Wait();
});
除了一个问题外,一切正常: 帐户列表中的第一个帐户不起作用。内部我必须使用多个请求(它不仅仅是登录)。第一个请求什么也没做。如果我打破调试器,则没有可用的源。
我有大约12个帐户。我试图从列表中删除第一个帐户。但问题仍然是相同的(现在新的第一个(旧的第二个)帐户失败)。
现在非常奇怪的一点: 如果我不使用Parallel.For,一切正常。
foreach (var acc in Accounts)
{
acc.WebProxy = null;
Debug.WriteLine("Connecting to {0}.", new object[] { acc.Username });
await acc.ConnectToIrc();
}
再次:除了列表中的第一个帐户外,一切正常。它始终是第一个(它不取决于列表包含多少帐户或哪个帐户是第一个帐户)。
有没有人有任何想法?
编辑:这就是我创建WebRequests的方式:
private async Task<string> GetResponseContent(HttpWebRequest request)
{
if (request == null)
throw new ArgumentNullException("request");
using (var response = await request.GetResponseAsync())
{
return await GetResponseContent((HttpWebResponse)response);
}
}
private async Task<string> GetResponseContent(HttpWebResponse response)
{
if (response == null)
throw new ArgumentNullException("response");
using (var responseStream = response.GetResponseStream())
{
return await new StreamReader(responseStream).ReadToEndAsync();
}
}
private HttpWebRequest GetRequest(string url)
{
if (String.IsNullOrWhiteSpace(url))
throw new ArgumentNullException("url");
try
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.CookieContainer = _cookieContainer;
request.Referer = url;
request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4 GTB6 (.NET CLR 3.5.30729)";
if (_webProxy != null)
request.Proxy = WebProxy.WebProxy;
request.KeepAlive = true;
request.Timeout = 30000;
return request;
}
catch (Exception ex)
{
ErrorLogger.Log(String.Format("Could not create Request on {0}.", url), ex);
return null;
}
}
答案 0 :(得分:3)
你正在遇到典型的等待死锁情况。问题是您对ConnectToIrc
的调用正在使用await
并捕获同步上下文。他们试图编辑主线程的延续。问题是您的主线程忙于阻止对Parallel.ForEach
的调用。它不允许任何延续运行。主线程正在等待继续继续,继续等待主线程可以自由运行。死锁。
这是(一个原因)为什么你不应该同步等待异步操作。
相反,只需启动所有异步操作并使用WhenAll
等待它们全部完成。无需创建新线程,也无需使用线程池等。
var tasks = new List<Task>();
foreach (var acc in Accounts)
{
acc.WebProxy = null;
Debug.WriteLine("Connecting to {0}.", new object[] { acc.Username });
tasks.Add(acc.ConnectToIrc());
}
await Task.WhenAll(tasks);
与第二个示例不同,这将并行执行所有异步操作,同时仍然异步等待。
答案 1 :(得分:0)
再次更新:
var tasks = Accounts.Select(MyTask).ToList();
await Task.WhenAll(tasks);
然后你可以编写一个命名方法:
private Task MyTask(Account acc)
{
acc.WebProxy = null;
Debug.WriteLine("Connecting to {0}.", new object[] { acc.Username });
return acc.ConnectToIrc();
}
感谢提示