C# 垃圾收集器、线程和 Compiler/Jitter 优化
C# Garbage Collector, Threading and Compiler/Jitter optimization
假设我们的程序有一个中心点(Document class 的一个实例),其中引用了各种信息。
现在我们有两个线程。两个线程都可以访问我们的 "document" 并且 "document" 包含对 "params" 的引用(一个包含某种信息的对象)。因此,如果我们有对 "document" 的引用,我们可以使用 "document.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 块,这是一个原子操作。
现在最大的问题是:
抖动是否有可能优化线程 1 的代码,使 tempParams 不是内存地址而是 CPU 寄存器?如果线程 2 更新 document.params 引用,则内存中没有指向旧 "Params" 块的引用,因为线程 1 中的引用仅在 CPU 寄存器中。如果垃圾收集器就在这一刻启动,它怎么能看到旧的 "Params" 块仍在使用中?
另一个问题是:抖动是否会优化掉 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 错误,IMO。不能保证线程 1 会看到对 document.params
的更改(因此在不同的情况下仍然需要考虑风险),但考虑到它 已经 将引用复制到本地变量 (tempParams
) 并且该变量永远不会更改其值,所有通过 tempParams
的访问都将 寻址同一个对象。 (没有 tempParams.a
从一个对象读取但 tempParams.b
从另一个对象读取的风险。)
只是为了将下面的一些评论带入这个答案 - 有一些关于 JIT 对 "optimize" 代码是否有效的讨论,这样它似乎会改变局部变量的值。 This MSDN article certainly suggests it would be valid, for example. I saw something similar and blogged about it a long time ago。我 99% 确定我和某人(可能是 Joe Duffy)讨论了有效阅读介绍是否对 ECMA-335 有效,他们的印象是事实并非如此。但是,我找不到任何明确的文档,而且 ECMA-335 至少 不清楚这个问题。
ECMA-335 (CLI) 规范肯定比 MS 已经实施了一段时间的 CLR 2.0 模型更宽松,但我不认为 [=32] =] 不严。如果你不能依赖于与变化隔离的局部变量,那么很难编写 任何 有效代码,IMO。
假设我们的程序有一个中心点(Document class 的一个实例),其中引用了各种信息。 现在我们有两个线程。两个线程都可以访问我们的 "document" 并且 "document" 包含对 "params" 的引用(一个包含某种信息的对象)。因此,如果我们有对 "document" 的引用,我们可以使用 "document.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 块,这是一个原子操作。
现在最大的问题是:
抖动是否有可能优化线程 1 的代码,使 tempParams 不是内存地址而是 CPU 寄存器?如果线程 2 更新 document.params 引用,则内存中没有指向旧 "Params" 块的引用,因为线程 1 中的引用仅在 CPU 寄存器中。如果垃圾收集器就在这一刻启动,它怎么能看到旧的 "Params" 块仍在使用中?
另一个问题是:抖动是否会优化掉 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 错误,IMO。不能保证线程 1 会看到对 document.params
的更改(因此在不同的情况下仍然需要考虑风险),但考虑到它 已经 将引用复制到本地变量 (tempParams
) 并且该变量永远不会更改其值,所有通过 tempParams
的访问都将 寻址同一个对象。 (没有 tempParams.a
从一个对象读取但 tempParams.b
从另一个对象读取的风险。)
只是为了将下面的一些评论带入这个答案 - 有一些关于 JIT 对 "optimize" 代码是否有效的讨论,这样它似乎会改变局部变量的值。 This MSDN article certainly suggests it would be valid, for example. I saw something similar and blogged about it a long time ago。我 99% 确定我和某人(可能是 Joe Duffy)讨论了有效阅读介绍是否对 ECMA-335 有效,他们的印象是事实并非如此。但是,我找不到任何明确的文档,而且 ECMA-335 至少 不清楚这个问题。
ECMA-335 (CLI) 规范肯定比 MS 已经实施了一段时间的 CLR 2.0 模型更宽松,但我不认为 [=32] =] 不严。如果你不能依赖于与变化隔离的局部变量,那么很难编写 任何 有效代码,IMO。