【问题标题】:The tasks argument included a null valuetasks 参数包含一个空值
【发布时间】:2015-12-19 00:40:28
【问题描述】:

我用的是mvc 5,跟着this sample生成一个数组Task。

我确定数据库至少包含 1 行的查询。我认为我遇到了 Task 的问题,因为它向我抛出了错误消息

tasks 参数包含一个空值。

当我尝试时:

// I'm sure `cg` was not null and `type` was not empty
var cg = new List<string>();
var type = "";

var db = new MyDbContext();
var list = new List<TopicViewModels>();

if (cg != null && cg.Count > 0)
{
   var tasks = new Task<List<TopicViewModels>>[13];
   byte i = 0;

   while (i < cg.Count)
   {
      string _cg = cg[i];

      tasks[i] = Task.Run(async () => 
      {
         return await db.Topics.Where(m => m.Type == type && m.Category == _cg)
            .ToListAsync();
      });

      i++;
   }

   var continuation = Task.WhenAll(tasks); //the tasks argument included a null value

   // never go to this loop...
   foreach (var topics in continuation.Result)
   {
      topics.ForEach(x => list.Add(x));
   }
}

我设置了断点来检查数组taskstasks[0] 不为空。它已正确附加。

你能解释一下为什么吗?

更新:(基于@YacoubMassad 评论)

await Task.WhenAll(tasks); //same error here...

//never go to this loop, too...
foreach (var task in tasks)
{
   //
}

更新 2:(基于 @DavidPine 的回答)

if (cg != null && cg.Count > 0)
{
   var tasks = new List<Task<List<TopicViewModels>>>();
   cg.ForEach(x =>
   {
      tasks.Add(Task.Run(async () =>
      {
         return await db.Topics.Where(m => m.Type == type && m.Category == x)
            .ToListAsync();
      }));
   });

   foreach (var topics in await Task.WhenAll(tasks.ToArray()))
   {
      topics.ForEach(x => list.Add(x));
   }
}

【问题讨论】:

  • 这段代码存在的方法是async吗?
  • @YacoubMassad 是的。它在public async Task&lt;ActionResult&gt; Topics(){}
  • 尝试将var continuation = Task.WhenAll(tasks);改为await Task.WhenAll(tasks);,然后循环遍历tasks数组
  • 我认为一个问题是cg.Count 可能小于 13
  • @YacoubMassad 我只是再试一次并更新我的问题。请检查

标签: c# .net task


【解决方案1】:

这里有几个问题:

1. 使用TaskTask&lt;T&gt; 时,请始终使用async / await(如果可用),即;您正在使用 .NET 4.5。

2. 当您调用Task.WhenAll(tasks) 时,数组中可能有一个为空的任务。您需要对此进行检查并正确处理。

3.您需要等待一切完成以确保工作完成,await Task.WhenAll(tasks)

var cg = new List<string>();
var type = "";

var db = new MyDbContext();
var list = new List<TopicViewModels>();

if (cg != null && cg.Count > 0)
{
   var tasks = new Task<List<TopicViewModels>>[13];
   byte i = 0;

   while (i < cg.Count)
   {
      string _cg = cg[i];

      tasks[i] = Task.Run(async () => 
      {
         // Isn't it possible that the where clause filters out and returns null?
         return await db.Topics
                        .Where(m => m.Type == type && m.Category == _cg)
                        .ToListAsync();
      });

      i++;
   }

   // Use await keyword to ensure that work is done
   var continuation = await Task.WhenAll(tasks.Where(t => t != null).ToArray());

   // never go to this loop...
   foreach (var topics in continuation.Result)
   {
      topics.ForEach(x => list.Add(x));
   }
}

【讨论】:

  • 太棒了!非常感谢!
  • 如果将任务存储在列表中而不是数组中会更好。这样,即使您有超过 13 个任务,您也不会遇到问题。
  • 代码的另一个大问题。 db 不是线程安全的,您同时从多个线程调用它。
【解决方案2】:

您的代码中有几个问题。

首先,您使用查询数据库的自然异步方法,您不需要在线程池线程上执行 then,这是多余的,尤其是在已经使用线程池线程来服务请求的 ASP.NET 中。

其次,您不是在等待Task.WhenAll 返回的任务,而是在使用Task.Resultforeach 循环中迭代时同步阻塞。

你的代码应该是:

var queryTasks = cg.Select(cg => db.Topics.Where(m => m.Type == type && m.Category == cg).ToListAsync());
return await Task.WhenAll(queryTasks);

请注意,EF DbContext 不是线程安全的,并且不允许在其上执行并发操作。如果是这种情况,您需要独立地 await 每个查询。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-06
    • 2019-11-13
    • 2021-05-28
    • 1970-01-01
    • 1970-01-01
    • 2012-04-23
    • 2021-07-17
    相关资源
    最近更新 更多