Parallel.For WebRequests - 第一个请求就是什么都不做

时间:2014-02-11 13:59:13

标签: c# httpwebrequest parallel.foreach

我有一个帐户列表。我想登录网站上的所有帐户。我想使用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;
    }
}

2 个答案:

答案 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();
    }

感谢提示