【问题标题】:Getting error The tasks argument included a null value. Parameter name: tasks in Task parallel library with parallel.foreach出现错误 tasks 参数包含一个空值。参数名称:Task 并行库中的tasks with parallel.foreach
【发布时间】:2019-02-13 10:19:51
【问题描述】:

我正在尝试执行多个内部调用一些 http 调用的任务。 问题是当我使用 Parallel.ForEach 循环时出现错误:

tasks 参数包含一个空值。参数名称:任务

List<Task> TskList = new List<Task>();

Parallel.ForEach(dt.AsEnumerable(), row =>
//foreach (DataRow row in dt.Rows)
{

    var oTsk =
        new Task(
            () =>
            {
                try
                {
                    some http call
                }
                catch (Exception ex)
                {

                    //AppendTextBox(row["ssub_msisdn"] as string + ", Error: " + ex.Message, txtBoxResponse);
                }
            });
    TskList.Add(oTsk);
    oTsk.Start();
}
);

var t = Task.WhenAll(TskList.ToArray());
try
{
    await t;
}
catch { }

if (t.Status == TaskStatus.RanToCompletion)
{
    SetLabel("Completed", lblProcessingStatus);
}
else if (t.Status == TaskStatus.Faulted)
{ SetLabel("Faulted", lblProcessingStatus); }

【问题讨论】:

  • 您不需要使用Parallel.ForEachTask.Run 来并行执行http 请求。向我们展示some http call 代码,我们可以向您展示如何改进它。
  • @KamranShahid:请描述你真正想要做什么。这段代码中有很多危险信号:永远不要使用 Task 构造函数和 Task.Start; HTTP 调用是基于 I/O 的,不需要Parallel.ForEach;有一个空的 catch 块;并且代码正在使用Task.Status 进行调试以外的其他事情。
  • @StephenCleary:catch 块正在尝试将错误消息添加到 TextBox,但由于调用是从后台线程完成的,因此这实际上从未起作用,因此该调用被注释掉了。

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


【解决方案1】:

您正试图从不同的线程访问列表TskList 而没有任何同步。这可能会导致任何类型的问题。

这样做:

var tasks = dt.AsEnumerable().Select(row => Task.Run(() =>
    {
        try
        {
            // some http call
        }
        catch (Exception ex)
        {
            // rewrap the needed information into your custom exception object
            throw YourException(ex, row["ssub_msisdn"]);
        }
    });

// now you are at the UI thread
foreach (var t in tasks)
{
    try
    {
        await t;
    }
    catch (YourException ex)
    {
        AppendTextBox(ex.SsubMsisdn + ", Error: " + ex.InnerException.Message, txtBoxResponse);
    }
}

Task.Run 会在线程池上启动任务,你实际上不需要Parallel.ForEach


实际上,如果您在try 中的代码只是进行http 调用,那么您根本不需要Task!您可以通过使用异步版本来完全避免线程,例如。 G。 HttpClient.GetByteArrayAsyncHttpClient.GetStreamAsync + Stream.CopyToAsync

E. g.:

HttpClient client = new HttpClient(); // maybe configure it

async Task ProcessRow(Row row) // put here the correct type
{
    try
    {
        var str = await client.GetStringAsync(row[address]);
        AppendTextBox(str, txtBoxResponse);
    }
    catch (HttpRequestException ex)
    {
        AppendTextBox(row["ssub_msisdn"] + ", Error: " + ex.Message, txtBoxResponse);
    }
}

var tasks = dt.AsEnumerable().Select(row => ProcessRow(row));
await Yask.WhenAll(tasks);

【讨论】:

  • 实现了细微的变化。非常感谢弗拉德
  • @KamranShahid:不客气!但是看看答案的最后一段(刚刚添加)。
  • 该调用是一个示例代码。实际上,我还在更新一些 ui 元素(通过 invokerequired)以及 http 调用发送以及收到 http 调用等的结果时。
  • @KamranShahid:如果您切换到异步版本,您将留在 UI 线程中并且不需要 InvokeRequired :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-02-06
  • 1970-01-01
  • 2020-03-11
  • 1970-01-01
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多