【问题标题】:Weak References stays alive弱引用仍然存在
【发布时间】:2015-03-06 14:53:34
【问题描述】:

我有一个接口:IRemoteDataChangedListener

public interface IRemoteDataChangedListener<TData>
{
    void DataReceived(TData newData);
}

还有一个类,RealtimeEventService

public class RealtimeEventService : IRealtimeEventService
{
    private readonly IEventListener listener;

    private readonly List<Tuple<Type, WeakReference>> dataCreated;

    public RealtimeEventService(IEventListener eventListener)
    {
        this.dataCreated = new List<Tuple<Type, WeakReference>>();
        this.listener = eventListener;
        this.listener.EventReceived += this.ListenerOnEventReceived;
    }

    private void ListenerOnEventReceived(EventMessage message)
    {
        Type type = message.GetType();

        if (type == typeof(NotificationReadEventMessage))
        {
            this.DataChanged((NotificationReadEventMessage)message);
        }
    }

    public void SubscribeDataChanged<TEventMessage>(IRemoteDataChangedListener<TEventMessage> dataChangedListener) where TEventMessage : EventMessage, new()
    {
        this.dataCreated.Add(Tuple.Create(typeof(TEventMessage), new WeakReference(dataChangedListener)));
    }

    internal void DataChanged<TKey>(TKey newData)
        where TKey : class, new()
    {
        LoopAndFilter<TKey>(this.dataCreated, listener => listener.DataReceived(newData));
    }

    private static void LoopAndFilter<TKey>(ICollection<Tuple<Type, WeakReference>> collection,
        Action<IRemoteDataChangedListener<TKey>> success) where TKey : class
    {
        foreach (var reference in collection.ToArray())
        {
            if (!reference.Item2.IsAlive)
            {
                collection.Remove(reference);
                continue;
            }

            if (reference.Item1 != typeof(TKey))
                continue;

            success((IRemoteDataChangedListener<TKey>)reference.Item2.Target);
        }
    }

    #endregion
}

每当我创建一个使用 NotificationReadEventMessage 作为通用参数继承 IRemoteDataChangedListener 的测试类,并将此类的实例与 SubscribeDataChanged() 一起使用时,它就会很好地连接起来,然后调用该方法。

问题是,当我将实例引用设置为 null 并运行 GC.Collect() 时,它应该为 null,并且下次运行 RealtimeEventService 的 LoopAndFilter 方法时,它应该检测到它不再存在,并删除列表中的弱引用。

然而事实并非如此。当我检查值(在 LoopAndFilter 中)时,在测试中将实例引用设置为 null 后,该值仍然显示为 Alive 为真。

现在我已经盯着这段代码看了好几个小时了,我根本找不到任何可以强烈引用该类的地方...

有什么帮助吗?

@Edit:单元测试(使用 Moq 和 Should 库):

public class RealtimeEventServiceTests
{
    [Fact]
    public void VerifyWeakReferencesWorksAsIntended()
    {
        var eventListenerMock = new Mock<IEventListener>();
        IRealtimeEventService service = new RealtimeEventService(eventListenerMock.Object);

        bool called = false;
        RemoteDataTest dataChangedListener = new RemoteDataTest();
        dataChangedListener.Called += (sender, args) => called = true;
        service.SubscribeDataChanged(dataChangedListener);

        called.ShouldBeFalse();
        ((RealtimeEventService)service).DataChanged(new NotificationReadEventMessage());
        called.ShouldBeTrue();

        called = false;
        dataChangedListener = null;
        GC.Collect();

        called.ShouldBeFalse();
        ((RealtimeEventService)service).DataChanged(new NotificationReadEventMessage());
        called.ShouldBeFalse();
    }
}

public class RemoteDataTest : IRemoteDataChangedListener<NotificationReadEventMessage>
{
    public event EventHandler Called;

    public void DataReceived(NotificationReadEventMessage newData)
    {
        if (Called != null) Called(this, null);
    }
}

【问题讨论】:

  • 尝试用真正的监听器替换你的匿名函数 => listener.DataReceived(newData) 通常它们可以隐藏“聪明”的编译器内容
  • 不是问题的根源,但您的 reference.Item2.IsAlive 代码很糟糕。您有一个竞争条件,对象可以在 IsAlive 检查和reference.Item2.Target 之间进行 GC。更好的选择是在检查项目是否被 GC 之前执行 var item = reference.Item2.Target; if(item == null) { collection.Remove(reference); continue } 以获得对该对象的潜在强引用(如果对象被收集,.Target 将返回 null)
  • 这很奇怪。当我在家用 PC 上运行这个完全相同的代码时,它通过就好了。我的家用电脑比我的工作电脑有更好的硬件,但这不应该影响代码,对吧?

标签: c# garbage-collection weak-references


【解决方案1】:

事实证明,当我回到家并在家里编译它时,它运行得很好。当我重新开始工作时,它在那里也工作得很好。

猜猜它只是那些在莫名其妙的时间神奇地消失的怪异虫子之一。我很高兴摆脱它。

我确实听取了 Ewan 和 Scott Chamberlain 的建议,非常感谢!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    相关资源
    最近更新 更多