是否有超时的弱引用实现?

时间:2021-05-26 16:10:50

Sometimes I need to hold a reference to object in memory during some time. Weak reference allows this but the time is limited by the garbage-collection cycle. The question is similar to this one but is related to .NET.

有时候我需要在一段时间内对内存中的对象进行引用。弱引用允许这样但时间受垃圾收集周期的限制。问题与此类似,但与.NET有关。

I know that there are many caches that have such feature built in but I don't need to utilize a full cache (which is a Key-Value map) to store only one object. It would rather be a single "expirable reference".

我知道有许多内置了这种功能的缓存但我不需要使用完整缓存(这是一个键值映射)来只存储一个对象。它宁愿是一个单一的“可预期参考”。

I know that I can implement this myself but I'm looking for a standard implementation (ideally integrated in the framework, in case I've missed it)

我知道我自己可以实现这个,但我正在寻找一个标准的实现(理想情况下集成在框架中,万一我错过了)

UPDATE

UPDATE

Based on the answers from @Jeroen Mostert and @dariogriffo I add more details to the question.

根据@Jeroen Mostert和@dariogriffo的答案,我在这个问题上添加了更多细节。

Ok, so there are two ways: either use key-value cache or timer-based callback. Lets assume there is a tree of objects each having a reference to a cached value. These values have an expiration timeout. Assume count of tree nodes is large. If I store each value in the MemoryCahce this requires to assign unique keys to the nodes and access to their values is made by hash table search. Also it has some computation complexity of adding new keys to the table. From the other hand if I use my custom WeakReferenceByTimeOut as @dariogriffo suggests then It doesn't have such problems. Instead, there is an overhead because I need to create one timer object per value object (or Task object that is using timer internally, I guess). From my point of view the second approach is better but I'm not sure.

好的,有两种方法:使用键值缓存或基于计时器的回调。让我们假设有一个对象树,每个对象都有一个缓存值的引用。这些值具有到期超时。假设树节点的数量很大。如果我将每个值存储在MemoryCahce中,则需要为节点分配唯一键,并且通过哈希表搜索来访问它们的值。此外,它还具有向表添加新密钥的一些计算复杂性。另一方面,如果我使用我的自定义WeakReferenceByTimeOut作为@dariogriffo建议那么它没有这样的问题。相反,有一个开销,因为我需要为每个值对象创建一个计时器对象(或者我在内部使用计时器的Task对象)。从我的观点来看,第二种方法更好,但我不确定。

2 个解决方案

#1


2  

There is nothing built in as far as I know either, so here goes a 1 minute implementation

据我所知,没有内置任何东西,所以这里进行了1分钟的实施

using System.Timers;

public class WeakReferenceByTimeOut<T> where T : class
{
    private Timer _timer;

    public WeakReferenceByTimeOut(T val, int miliseconds)
    {
        Reference = val;
        _timer = new Timer(miliseconds);
        _timer.Elapsed += KillReference;
        _timer.AutoReset = false;
        _timer.Enabled = true;
    }

    public T Reference { get; private set; }

    private void KillReference(object sender, ElapsedEventArgs e)
    {
        _timer.Elapsed -= KillReference;
        _timer.Dispose();
        _timer = null;

        Reference = null;   
    }        
}

#2


2  

If it's important to your requirements that an entry should not be valid for more than a certain amount of time, well, you do need a cache of some sort, even if it's just a cache of one. The simplest implementation (if you never set the reference to anything else) is Task.Delay(...).ContinueWith(t => myWeakReference = null), which requires no additional class. If you do need the ability to set the reference to something else (which means resetting the timer) you'll need a wrapper class.

如果一个条目在一段时间内不应该有效对你的要求很重要,那么你确实需要某种缓存,即使它只是一个缓存。最简单的实现(如果你从未设置其他任何引用)是Task.Delay(...)。ContinueWith(t => myWeakReference = null),它不需要额外的类。如果你确实需要能够将引用设置为其他东西(这意味着重置计时器),你需要一个包装类。

I don't recommed writing a class with support for setting the reference to something else, by the way -- there is an inherent race condition between setting the reference and clearing it just when the timer expires. If you need something like this, a vetted cache implementation with policies is almost certainly better, even if you do use it for just one reference.

我不建议编写一个支持将引用设置为其他东西的类,顺便说一下 - 在设置引用和在计时器到期时清除它之间存在固有的竞争条件。如果您需要这样的东西,那么带有策略的经过审查的缓存实现几乎肯定会更好,即使您只将其用于一个参考。

There is nothing like this in the BCL; it's an unusual scenario. Usually you would either cache multiple values, or you don't care when exactly the reference gets cleaned up as long as the cache stays within certain memory limits. If you have a cache with a timeout policy, like System.Runtime.Caching.MemoryCache, you can store WeakReference instances in that, which should achieve the same thing (but you need to double check that the entry you get back from the cache is still valid).

BCL中没有这样的东西;这是一个不寻常的场景。通常,您可以缓存多个值,或者只要缓存保持在某些内存限制内,您就不关心何时清理引用。如果你有一个带有超时策略的缓存,比如System.Runtime.Caching.MemoryCache,你可以在其中存储WeakReference实例,这应该实现相同的功能(但你需要仔细检查你从缓存中获取的条目是仍然有效)。

#1


2  

There is nothing built in as far as I know either, so here goes a 1 minute implementation

据我所知,没有内置任何东西,所以这里进行了1分钟的实施

using System.Timers;

public class WeakReferenceByTimeOut<T> where T : class
{
    private Timer _timer;

    public WeakReferenceByTimeOut(T val, int miliseconds)
    {
        Reference = val;
        _timer = new Timer(miliseconds);
        _timer.Elapsed += KillReference;
        _timer.AutoReset = false;
        _timer.Enabled = true;
    }

    public T Reference { get; private set; }

    private void KillReference(object sender, ElapsedEventArgs e)
    {
        _timer.Elapsed -= KillReference;
        _timer.Dispose();
        _timer = null;

        Reference = null;   
    }        
}

#2


2  

If it's important to your requirements that an entry should not be valid for more than a certain amount of time, well, you do need a cache of some sort, even if it's just a cache of one. The simplest implementation (if you never set the reference to anything else) is Task.Delay(...).ContinueWith(t => myWeakReference = null), which requires no additional class. If you do need the ability to set the reference to something else (which means resetting the timer) you'll need a wrapper class.

如果一个条目在一段时间内不应该有效对你的要求很重要,那么你确实需要某种缓存,即使它只是一个缓存。最简单的实现(如果你从未设置其他任何引用)是Task.Delay(...)。ContinueWith(t => myWeakReference = null),它不需要额外的类。如果你确实需要能够将引用设置为其他东西(这意味着重置计时器),你需要一个包装类。

I don't recommed writing a class with support for setting the reference to something else, by the way -- there is an inherent race condition between setting the reference and clearing it just when the timer expires. If you need something like this, a vetted cache implementation with policies is almost certainly better, even if you do use it for just one reference.

我不建议编写一个支持将引用设置为其他东西的类,顺便说一下 - 在设置引用和在计时器到期时清除它之间存在固有的竞争条件。如果您需要这样的东西,那么带有策略的经过审查的缓存实现几乎肯定会更好,即使您只将其用于一个参考。

There is nothing like this in the BCL; it's an unusual scenario. Usually you would either cache multiple values, or you don't care when exactly the reference gets cleaned up as long as the cache stays within certain memory limits. If you have a cache with a timeout policy, like System.Runtime.Caching.MemoryCache, you can store WeakReference instances in that, which should achieve the same thing (but you need to double check that the entry you get back from the cache is still valid).

BCL中没有这样的东西;这是一个不寻常的场景。通常,您可以缓存多个值,或者只要缓存保持在某些内存限制内,您就不关心何时清理引用。如果你有一个带有超时策略的缓存,比如System.Runtime.Caching.MemoryCache,你可以在其中存储WeakReference实例,这应该实现相同的功能(但你需要仔细检查你从缓存中获取的条目是仍然有效)。