是否可以抢占构造函数
本文关键字:构造函数 是否 | 更新日期: 2023-09-27 18:31:13
函数是否可以在 C# 中被抢占?
例如,考虑以下代码:
public class A
{
public bool ready = true;
public A()
{
ready = false; // Point #1
// Other initialization stuff
ready = true; // Point #2
}
}
在代码中的其他地方,两个线程可以访问 A 类型的变量,第一个线程调用在点 #1 处被抢占的构造函数。然后第二个线程测试ready
并发现它仍然是真的,因此它做了一些不好的事情。
这种情况可能吗?
更具体地说:
- 是否可以抢占构造函数?
- 如果是这样,这是否意味着构造函数中应该有同步代码,例如
lock
? - 正在构造的对象是否仅在构造函数退出后分配给共享变量,从而完全避免了这个问题?
我认为接受的答案不正确。Igor Ostrovsky 的"理论与实践中的 C# 内存模型"(此处为 2 部分)彻底解释了这些问题,其中一个示例准确地说明了您所问的内容。
它给出了代码
class BoxedInt2
{
public readonly int _value = 42;
void PrintValue()
{
Console.WriteLine(_value);
}
}
class Tester
{
BoxedInt2 _box = null;
public void Set() {
_box = new BoxedInt2();
}
public void Print() {
var b = _box;
if (b != null) b.PrintValue();
}
}
并注意:
由于 BoxedInt 实例发布不正确(通过非易失性字段,_box),因此调用 Print 的线程可能会观察到部分构造的对象!同样,使_box字段易变将解决此问题。
我强烈建议阅读整篇文章,这是一本非常有用的读物。
正在构造的对象是否仅分配给共享变量 在构造函数退出后,从而完全避免了这个问题?
是的
。如果仅在构造函数返回时才构造对象,则引用的值将分配给变量。因此,只有在该之后,另一个线程才能调用以检查值是true
还是false
。
两个线程永远不能同时进入构造函数。
安全发布问题 - 发布未完全创建的对象
但是,如果您正在发布对某些共享列表的当前引用,则很容易出错。注意不要在构造函数中做这样的事情
A(List sharedList){
sharedList.add(this);
//initializing instance variables
}
请原谅Java代码。因此,在这种情况下,您将不完整的创建对象发布到共享列表,该共享列表可以被其他线程访问,并可能导致许多问题。
不要在构造期间发布"this"引用,Java。不确定是否同样适用于 C#
然后让我回到最初的问题,在 C# 中可以 构造函数被抢占?
就抢占而言,是的,IMO 可以在构造函数运行时中断线程。构造函数就像一个普通的方法,但具有特殊的语义。
这根本不影响代码的线程安全性,因为只有一个线程可以位于对象的构造函数中。因此,它是完全线程安全的,直到您不会逃脱未完全构建的this
引用。
如果是这样,这是否意味着应该有这样的同步代码 作为构造函数中的锁定?
构造函数中不能只有一个线程,因此它本质上是线程安全的。