在我的应用程序中(用C#编写)我有一个类的实例,其中一个成员变量指向另一个类的实例。第二个实例是只读的,因此该实例的状态在创建后永远不会更改。但在某些情况下,我想用一个新的更新实例替换此实例。在多线程环境中,在第一个实例中替换此引用是否安全?或者这会导致问题吗?
答案 0 :(得分:9)
简单的写操作本身就是线程安全的。
所以这是好的:
x.y = new Y(); // OK
但以下不是:
if (x.y == null)
x.y = new Y(); // might overwrite a non-null x.y
所以这取决于你想要如何使用它,以及线程的期望。但原子性意味着你在x.y
答案 1 :(得分:6)
是的,在参考读/写是原子的意义上它是“安全的”。您的客户端代码将始终获得有效的实例。
显然,您无法保证客户端代码会立即获得新实例。
答案 2 :(得分:3)
引用替换是32位环境下的32位整数写操作,64位环境下的64位整数写操作。写操作确实分两步执行:
我认为默认情况下它不是线程安全的
答案 3 :(得分:3)
在第一个实例中替换此引用是安全的 多线程环境?
很抱歉在这里wishy-washy,但这取决于你如何定义“安全”。
意外行为可分为三大类。
案例#1:因此,在第一种情况下,两个线程可以竞争以虚假借口进行分配。最好的例子是创建一个单身人士。
public static Singleton GetSingleton()
{
// More than one thread could read null and attempt instantiation here.
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
案例#2:在第二种情况下,即使另一个线程发布了新实例,一个线程也可能继续读取旧实例。考虑这个例子。
public class Example
{
private SuperHero hero = new Superman();
void ThreadA()
{
while (true)
{
Thread.Sleep(1000);
hero = new CaptainAmerica();
Thread.Sleep(1000);
hero = new GreenLantern();
Thread.Sleep(1000);
hero = new IronMan();
}
void ThreadB()
{
while (true)
{
hero.FightTheBadGuys();
}
}
}
问题在于,编译器或硬件可能会通过“解除”循环外部ThreadB
的读取来优化hero
。
void ThreadB()
{
SuperHero local = hero;
while (true)
{
local.FightTheBadGuys();
}
}
不难看出我们的一个超级英雄会变得非常疲惫。然后,他又是一个超级英雄,所以事后可能没有问题。我想在这里提出的观点是,这个 staleness 工件可能会或可能不会成为问题,具体取决于您的情况。
案例#3:在第三种情况下,线程可能错误地认为变量将在两次读取之间引用相同的实例。
void SomeThread()
{
var result1 = instance.DoSomething();
// The variable instance could get changed here!
var result2 = instance.DoSomethingElse();
// Are result1 and result2 in a mutually consistent state here?
}
在上面的示例中,result1
和result2
可能不处于相互一致的状态。这是因为它们来自在两个不同实例上运行的操作。同样,这可能是也可能不是问题,具体取决于您的具体情况。
答案 4 :(得分:1)
不,由于缓存(某些架构需要像这样的内存屏障),编译器优化和并发访问,它不是线程安全的。
您有以下选择:
volatile
。Interlocked.Exchange()
替换值。阅读MSDN文档以选择更适合您的方法(快速:volatile 应该更快,但您无法保存旧值并在一次原子操作中编写新值。)
<强> EDITED 强>
我想我必须明确我的意思(许多人似乎认为 atomic 是线程安全的同义词):
在多线程环境中,在第一个实例中替换此引用是否安全?
不,严格来说,这不是线程安全的。你不知道会发生什么,所以它不安全。
或者这会导致问题吗?
这取决于具体情况。如果一个线程不使用正确的套接字连接(或一个封闭的连接),那么这将是一个大问题。如果一个线程不使用正确的颜色来格式化其输出,那么可能不会成为问题。
答案 5 :(得分:0)
使用以下代码,您希望使用新实例更新对象。它是线程安全的代码。
MyClass obj = null;
Interlocked.CompareExchange(ref obj, new MyClass(), null);