c# - Parallel.Foreach 和每个产生不同的结果 : Why is my code

我有一个文本文件,我将其读取为字符串 content。为了识别我想进一步处理的文本主体,我获取了字符串中关键字的索引,然后将“起始”索引设置为找到的最小索引。

我用 Parallel.ForEach 试过了 ...

ConcurrentBag<int> indexes = new();
int index;

switch (Case)
{
    case 1:
        Parallel.ForEach(KeywordTypes.GetImplementedNamedObjects(), inos =>
        {
            index = content.IndexOf($"/begin {inos}");
            index = index == -1 ? content.Length : index;
            indexes.Add(index);
        });
        index = indexes.Min();
        return index;

...和foreach:

foreach (string inos in KeywordTypes.GetImplementedNamedObjects())
{
    index = content.IndexOf($"/begin {inos}");
    index = index == -1 ? content.Length : index;
    indexes.Add(index);
}

index = indexes.Min();
return index;

其中 foreach 产生预期结果,但 Parallel.ForEach 没有。

为什么我的代码不是线程安全的?

最佳答案

这里只有一个index变量,因为它被“捕获”了。这意味着多个线程可以为它争吵,而不是每个线程都有自己的版本。

考虑:

  • 线程 A 计算 index = content.IndexOf($"/begin {inos}");
  • 线程 B 计算 index = content.IndexOf($"/begin {inos}"); - 糟糕,线程 A 的版本刚刚被覆盖
  • 线程 A 计算 index = index == -1 ? content.Length : index; 使用 B 刚刚更新的 index
  • 等等

重点是:由于线程争用而丢失了一个值。

只需移动 index 的声明即可解决此问题:

Parallel.ForEach(KeywordTypes.GetImplementedNamedObjects(), inos =>
{
    var index = content.IndexOf($"/begin {inos}");
    ...

从根本上说,变量的作用域是由声明它的地方定义的。如果变量在局部方法/lambda 的外部 声明,编译器会尊重您的要求,并且该变量在该局部方法/lambda 的所有使用之间共享;如果它是在本地方法/lambda 中内部声明的,则生命周期是该调用的本地,调用者之间不共享任何状态。

如果你想绝对确定你没有意外泄漏状态,lambda 上的 static 修饰符可以实现这一点,尽管它也可以防止访问 indexes,所以...可能不是您在这里需要的。

关于c# - Parallel.Foreach 和每个产生不同的结果 : Why is my code unsafe?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69158225/

相关文章:

python - Azure Databricks python 命令显示当前集群配置

c - 栈在使用Pthread的多线程程序中是如何工作的?

docker - 使用 deps.edn 在容器中安装依赖项

java - 为什么 java 编译器不提示 "\s"?

c# - 如何在 C# 中使用预处理器指令仅在 Windows 10 上执行一些代码?

android - 从协程(kotlin)切换到隔离(dart)

c++ - 在 constexpr 分支中使用枚举类值

laravel - Laravel 中的 getClientOriginalExtension()

python - 如何找到中轴的关节和端点

html -

  • 上的背景图像变为背景而不是列表元素符号