【问题标题】:Why am I receiving an ArgumentException with this piece of code?为什么我会收到带有这段代码的 ArgumentException?
【发布时间】:2014-07-31 04:43:33
【问题描述】:

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

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 秒,因此我希望并行发出请求以尽可能减少该时间。如果有更好的方法,请告诉我。

【问题讨论】:

  • 我不确定是否是这样,但您知道您在该行缺少右括号吗? await Task.WhenAll(requestTasks).ConfigureAwait( 应该是 await Task.WhenAll(requestTasks).ConfigureAwait()
  • 你的HttpSocket线程安全吗?
  • 您到底为什么要将requestTasks.Add 放入Parallel.For?这样做你什么也得不到,实际代码应该更慢,GetAsync 的重点是你在做I/O async。线程并行化只会减慢速度。
  • @Aron 我要做的是并行发出多个请求。我正在做一个个人项目,我正在尝试学习如何同时提出多个请求以加快执行速度。正常的 async-await 给了我不满意的表现。我应该在 Parallel.ForEach 块内执行同步请求吗?
  • 不,你错了。您正在制作线程以发送请求。但是上下文切换的过程很慢。发送请求很快,所以按顺序发送会更快。

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


【解决方案1】:

绝对没有充分的理由使用多个线程来初始化任务列表。开销大于收益。你可以改用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 指南。

【讨论】:

  • 我想我的目的是并行发出多个请求。在Parallel.ForEach 内进行同步工作会更好吗?我的印象是,这样做可以让任务遵循正常的 async-await 过程,同时也可以在单独的线程上并行执行。
  • @xCrispy 正常的异步行为意味着没有额外的线程。这意味着我们使用 IO 绑定工作的异步性质。您只使用Parallel.Foreach 将操作添加到列表中。这是完全没有必要的。
  • 那么,我还是在 Parallel.ForEach 内部进行同步工作而不是仅仅使用它来将任务添加到列表中会更好吗?在里面做HttpSocket.Instance.Get("http://google.com")会更有意义吗?
  • 没有。 IO 绑定操作不需要使用线程。你的Parallel.ForEach 是多余的,你根本不需要它
  • 很可能,是的。你已经掌握了窍门。当然,您会发现 TPL 对 async-await 有用的情况,但在大多数情况下,您的评论是正确的
【解决方案2】:

普通的List&lt;T&gt; 不是线程安全的。而你Adding 来自潜在的 50 个并行线程。那是行不通的。

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

【讨论】:

  • 更重要的是初始化 50 个 I/O 异步操作不需要线程并行化。
  • 这确实是我遇到异常的原因。非常感谢您帮助我解决这个问题。我刚刚接触到异步编程和多线程,所以很多东西,比如线程安全容器,对我来说仍然很陌生。
【解决方案3】:

正如 nvoigt 所说,List&lt;T&gt; 不支持被多个线程写入。幸运的是,.net 已经包含一组集合,这些集合支持 System.Collections.Concurrent (http://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);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    • 2017-06-28
    • 1970-01-01
    • 1970-01-01
    • 2012-08-10
    • 2016-09-05
    • 1970-01-01
    相关资源
    最近更新 更多