C# 垃圾收集器、线程和编译器/抖动优化

标签 c# multithreading reference garbage-collection

假设我们的程序有一个中心点(Document 类的实例),其中引用了各种信息。 现在我们有两个线程。两个线程都可以访问我们的“文档”,并且“文档”包含对“params”(保存某种信息的对象)的引用。因此,如果我们有对“document”的引用,我们可以使用“document.params”来获取我们的params对象。

线程 1 执行以下操作:

Params tempParams = document.params; // get a local reference to documents.params
int a = tempParams.a; // read data from params
// thread 1 (this thread) gets interrupted by thread 2
int b = tempParams.b; // read data from params
int c = tempParams.c; // read data from params

线程 2 执行以下操作:

Params newParams = new Params();
... // fill newParams with new parameters
lock(obj) {
    document.params = newParams; // update params in document
}

因此“params”的内容永远不会更改,但如果需要更改,则会生成一个新副本,并将引用“document.params”更新为新的 Params block ,这是一个原子操作。

现在最大的问题是:

抖动是否有可能优化线程 1 的代码,使 tempParams 不是内存地址而是 CPU 寄存器?如果线程 2 更新了 document.params 引用,则内存中没有指向旧“Params” block 的引用,因为线程 1 中的引用仅位于 CPU 寄存器中。如果就在此时垃圾收集器启动,它怎么能看到旧的“Params” block 仍在使用中?

另一个问题是:抖动是否会优化掉 tempParams 变量并直接使用 document.params.a/b/c。在这种情况下,线程 1 会看到 Params 对象的交换,这是不希望的。 tempParams 的使用应确保线程 1 从复制引用时位于 document.params 中的同一 Params 对象访问 a/b/c。

最佳答案

Is it possible that the jitter might optimize the code of thread 1 that way that tempParams is not a memory address but a CPU register?

我怀疑这是可能的 - 但这不会阻止垃圾收集器将其视为引用的使用。如果确实如此,那就是一个 GC 错误。

The other question would be: might it happen that the jitter optimizes away the tempParams variable and uses document.params.a/b/c directly.

在我看来,这将是一个 JIT 错误。无法保证线程 1 会看到 document.params 的更改(因此在不同场景中这仍然是需要考虑的风险),但考虑到它已将引用复制到局部变量 (tempParams) 并且该变量永远不会更改其值,通过 tempParams 进行的所有访问都将寻址同一对象。 (不存在 tempParams.a 从一个对象读取但 tempParams.b 从另一对象读取的风险。)

只是为了将下面的一些评论带入这个答案 - 有一些关于 JIT 是否有效“优化”代码以使其看起来改变局部变量的值的讨论。 This MSDN article例如,当然表明它是有效的。我看到something similar并在博客上介绍了它a long time ago 。我 99% 确信我曾与某人(可能是 Joe Duffy)讨论过 ECMA-335 有效的阅读介绍是否有效,但他们的印象是无效。但是,我找不到任何明确的文档,并且 ECMA-335 至少对此问题不清楚。

ECMA-335 (CLI) 规范绝对比 CLR 2.0 更宽松MS 已经实现了一段时间的模型,但我不认为它有那么松懈。如果您不能依赖于局部变量与更改隔离,那么就很难编写任何有效代码,IMO。

关于C# 垃圾收集器、线程和编译器/抖动优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31831598/

相关文章:

c++ - 包含引用的 STL 映射无法编译

c# - 有没有办法在 where 子句中访问私有(private)属性(property)?

c# - 3D 点线上的 3D 垂直点

c# - 在 Cookie 内检索从 C# 到 jquery

java - 将父类(super class)的对象转换为子类的对象会创建一个新对象吗?

c++ - 在基于范围的 for 循环中 & 和 && 有什么区别?

C# 为什么方法 HtmlDocument.GetElementById 匹配标签的属性名称?

java - OutputStream 未转换为字符串变量

c# - 如何在单独的线程中执行自定义类的方法调用?

multithreading - Delphi线程返回值