我有一个我从两个线程访问的对象。一个线程在返回值的对象上调用长时间运行的成员函数。第二个线程更新用于生成该值的对象。
我是否在第一个线程执行时调用Interlock.Exchange来替换第二个线程中的对象: 1.旧线程的自我保留对原始对象的引用。 2.是否存在原始对象被垃圾收集的风险?
import System;
import System.Threading;
import System.Generics;
class Example {
var mData = new String("Old");
public void LongFunction() {
Thread.Sleep(1000);
Console.WriteLine(mData);
}
public void Update() {
Interlocked.Exchange(ref mData, "Old");
}
}
class Program {
public static Main(string[] argv) {
var e = new Example();
var t = new Thread(new ThreadStart(e.LongFunction()));
t.Start();
e.Update();
}
}
这是否保证始终打印“旧”? 感谢。
答案 0 :(得分:2)
对象没有被垃圾收集的风险,因为它仍然在旧线程的调用堆栈中被引用。
修改强> 从你的代码中,mData用“Old”初始化,Update()用“Old”覆盖它,所以它确实会打印“Old”。
如果你的意思是:
public void Update()
{
Interlocked.Exchange(ref mData, "New");
}
然后打印结果可以是“新”或“旧”,但很可能是“新”,因为您在打印值之前等待1秒。
我不确定我是否了解您的代码示例与垃圾回收问题之间的关系。
答案 1 :(得分:0)
除了你忘记打电话给t.Start(),是的。
您永远不必担心变量是从您下面收集的垃圾。如果您可以获得对象的引用,则不会对其进行垃圾回收。
答案 2 :(得分:-1)
您的问题似乎是在C#中使用Interlocked.Exchange对象类型的陷阱之一。垃圾收集器也不是那个寻找麻烦的地方。
首先,请记住,您直接更换内存。如果您的mData变量的类型是Disposable,则只能处理最新的副本。互锁中断对象终结。
更令人担忧的是,如果要在mData对象上调用成员函数,这需要很长时间才能执行。这会在成员函数执行时更改self的值。
以下是一些代码,显示了在对象上使用Exchange的问题: 使用系统; 使用System.Collections.Generic; 使用System.Threading;
namespace ConcurrentTest
{
class ValType : IDisposable
{
public int I { get; set; }
public int Mem()
{
Thread.Sleep(1000);
return I;
}
void IDisposable.Dispose()
{
Console.WriteLine("Destroying");
}
}
class Example
{
ValType mData = new ValType {I = 0};
public void Print()
{
Console.WriteLine(mData.I);
Thread.Sleep(2000);
Console.WriteLine(mData.Mem());
}
public void Update()
{
var data = new ValType() { I = 1 };
Interlocked.Exchange(ref mData, null);
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
class Program
{
static void Main(string[] args)
{
var e = new Example();
var t = new Thread(new ThreadStart(e.Print));
t.Start();
e.Update();
t.Join();
Console.ReadKey(false);
}
}
}