为什么我用这段代码收到ArgumentException?

时间:2014-07-31 04:43:33

标签: c# .net multithreading task-parallel-library async-await

有人可以向我解释为什么我会从这段代码中收到ArgumentException吗?它告诉我tasks函数中的Task.WhenAll参数包含空值。但是,它只在我运行它时偶尔发生。

static async Task TestRequestSpeed()
{
    var requestTasks = new List<Task>();

    await Task.Run(() =>
    {
        Parallel.For(0, 50, (i) =>
        {
            requestTasks.Add(HttpSocket.Instance.GetAsync("http://google.com"));
        });
    });

    await Task.WhenAll(requestTasks).ConfigureAwait(false);
}

我不明白其中一个请求最终会返回一个空任务。如果有人能指导我解决潜在的问题,我将不胜感激!

更新

我应该澄清一下,我的意图是并行提出多个请求以加快执行速度。我正在处理一个个人项目,需要在单个方法调用中完成200个请求,我希望减少发出这些请求所需的时间。使用正常的async-await程序执行此操作目前大约需要45秒,因此我希望并行地发出请求以尽可能减少时间。如果有更好的方法,请告诉我。

3 个答案:

答案 0 :(得分:4)

使用多个线程来初始化任务列表绝对没有充分的理由。开销超过收益。您可以改为使用Enumerable.Select

static async Task TestRequestSpeedAsync()
{
   var requestTasks = Enumerable.Range(0, 50).Select(HttpSocket.Instance.GetAsync("http://www.google.com"));

   await Task.WhenAll(requestTasks).ConfigureAwait(false);
}

注意我添加了Async后缀以遵循TAP指南。

答案 1 :(得分:2)

普通List<T>不是线程安全的。而且你是Add来自潜在的50个并行线程。那不会起作用。

按顺序插入该列表,或使用线程安全的容器进行添加。

答案 2 :(得分:1)

正如nvoigt所说,List<T>不能支持被多个线程写入。幸运的是,.net已经包含一组支持System.Collections.Concurrenthttp://msdn.microsoft.com/en-us/library/system.collections.concurrent%28v=vs.110%29.aspx)命名空间下的线程安全写入的集合。

作为一个例子,这似乎对我有用:

static async Task TestRequestSpeed()
{
    var requestTasks = new ConcurrentQueue<Task>();

    await Task.Run(() =>
    {
        Parallel.For(0, 50, (i) =>
        {
            HttpClient hc = new HttpClient();
            requestTasks.Enqueue(hc.GetStringAsync(new Uri("http://google.com")));
        });
    });

    await Task.WhenAll(requestTasks).ConfigureAwait(false);
}