首页 > 解决方案 > 启动 ForEach 内的任务调试 NullReferenceException

问题描述

我正在启动一些任务来匹配长字符串中的正则表达式。

我的任务列表如下所示:

var splittingTasks = new List<Task>();
foreach (var keyword in _general.Keywords)
{
    splittingTasks.Add(Task.Run(() => SplitMatches(keyword)));
}
await Task.WhenAll(splittingTasks);

SplitMatches方法如下所示:

ConcurrentBag<string> _objectifiableMatches = new();

//...

public void SplitMatches(string keyword)
{
    string patternObject = $@"\/begin {keyword}[\s\S]+?\/end {keyword}";
    Regex regexObject = new(patternObject);
    MatchCollection Matches = regexObject.Matches(_content);
    Parallel.ForEach(Matches, m =>
    {
        var replacedQuotes = m.Value.Replace($"\"", "'");
        _objectifiableMatches.Add(replacedQuotes);
    });
}

await Task.WhenAll(splittingTasks);将结果调试到NullReferenceExceptionfor keyword: 'keyword' threw an exception of type 'System.NullReferenceException'。尽管如此,结果还是如预期的那样。

我读了这篇文章,其中说 for 循环中的 lambda 表达式可能会导致问题,因为不会立即评估委托。但即使在foreach循环内复制变量后,我仍然遇到同样的错误。

foreach (var kw in _general.Keywords)
{
    string keyword = kw;
    splittingTasks.Add(Task.Run(() => SplitMatches(keyword)));
}

你有什么建议吗?

编辑:

我的关键字列表是通过选项模式_general.Keywords从文件中导入的。appsettings.json但是,尝试以下方法会产生相同的调试错误:

List<string> keywords = new()
{
    "keyword1",
    "keyword2",
    "keyword3",
    "keyword4",
    "keyword5"
};

foreach (var kw in keywords)
{
    string keyword = kw;
    splittingTasks.Add(Task.Run(() => SplitMatches(keyword)));
}

EDIT2:如果我错了,请纠正我,但我认为错误仅来自调试。由于任务存在于循环之外但keyword不存在。因此调试器尝试访问未在作用域中声明的关键字的最后一个值。keyword在外部声明变量foreach不会产生错误,但结果却是完全错误的。我可以忽略这个错误吗?

编辑3:

foreach (var task in splittingTasks)
{
    Console.WriteLine(task.IsCompletedSuccessfully.ToString());
}

返回true所有任务。而且没有比预期更多的任务!(对于第一个编辑中提到的列表,它将是 5 个任务)

EDIT4: 我上传了一个简短的视频,展示了 Visual Studio 的问题,
在我看来,VSkeyword甚至在它在 for 循环中声明之前就正在评估该变量。我不明白为什么,我什至找不到抛出这个错误的方法。

EDIT5:您可以在此处
找到一个最小的可重现示例 在 Visual Studio 中复制粘贴并尝试调试该方法GetKeywordMatchesAsync()

标签: c#debuggingtasknullreferenceexceptionparallel.foreach

解决方案


在我看到视频后,我看到您将断点置于变量 (1) 的范围之外。

您的代码是绝对正确的,但我想知道您为什么要将代码并行化两次。在我看来,您不必这样做,但这是一个不同的话题。

当您在关键字变量初始化之后放置断点时,您将获得一个值 (2),如下所示: 在此处输入图像描述


推荐阅读