1

我有一个从两个线程访问的对象。一个线程在返回值的对象上调用一个长时间运行的成员函数。第二个线程更新用于产生该值的对象。

如果我在第一个线程执行时调用 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();
    }
}

这是否保证总是打印“旧”?谢谢。

4

3 回答 3

2

对象没有被垃圾回收的风险,因为它仍然在旧线程的调用堆栈中被引用。

编辑: 从您的代码中,mData 用“Old”初始化,Update() 用“Old”覆盖它,所以它确实会打印“Old”。

如果你的意思是:

public void Update()
{
    Interlocked.Exchange(ref mData, "New");
}

然后打印的结果可以是“新”或“旧”,但很可能是“新”,因为您在打印值之前等待 1 秒。

我不确定我是否理解您的代码示例和您的垃圾收集问题之间的关系。

于 2009-11-21T17:18:39.287 回答
0

除了你忘记调用 t.Start(),是的。

您永远不必担心从您下面收集的变量是垃圾。如果你能得到一个对象的引用,它就不会被垃圾回收。

于 2009-11-21T19:56:28.050 回答
-1

您的问题似乎是在 C# 中对对象类型使用 Interlocked.Exchange 的陷阱之一。垃圾收集器不是寻找麻烦的地方。

首先,请记住,您直接更换内存。如果您的 mData 变量的类型是 Disposable,则只能处理最新的副本。互锁中断对象最终确定。

更令人担忧的是,如果您在 mData 对象上调用成员函数,这需要很长时间才能执行。这会在成员函数执行时更改 self 的值。

下面是一些代码,显示了在对象上使用 Exchange 的问题: using System; 使用 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);
        }
    }
}
于 2009-11-22T04:17:40.273 回答