Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/257.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在析构函数中访问ConcurrentDictionary_C#_.net_Multithreading_Concurrency - Fatal编程技术网

C# 在析构函数中访问ConcurrentDictionary

C# 在析构函数中访问ConcurrentDictionary,c#,.net,multithreading,concurrency,C#,.net,Multithreading,Concurrency,我有一个包装器,其中T:class包装了我的对象。我将WeakReference存储在ConcurrentDictionary中,为不可变对象实现弱引用线程安全缓存,当其他对象需要内存时,这些对象会自动清除。我需要调用Wrapper析构函数中的ConcurrentDictionary.TryRemove,以释放字典中不再指向有效对象的弱引用 众所周知,我们不应该在析构函数中使用任何锁,因为存在死锁的风险。所以我想知道,我能在析构函数中安全地使用ConcurrentDictionary.TryRe

我有一个
包装器,其中T:class
包装了我的对象。我将
WeakReference
存储在
ConcurrentDictionary
中,为不可变对象实现弱引用线程安全缓存,当其他对象需要内存时,这些对象会自动清除。我需要调用
Wrapper
析构函数中的
ConcurrentDictionary.TryRemove
,以释放字典中不再指向有效对象的弱引用


众所周知,我们不应该在析构函数中使用任何锁,因为存在死锁的风险。所以我想知道,我能在析构函数中安全地使用
ConcurrentDictionary.TryRemove
?我担心它可能是使用
SpinLock
或其他工具实现的,因此在析构函数中使用时仍然存在死锁的风险。

因此,您不想在析构函数中使用锁的原因是,在正在所有线程上停止执行

这样,如果一个线程处于一个并发的二进制操作中,当终结器被开除时,如果析构函数试图锁定并发字典(可能是一个非常难复制的死锁),则可能会死锁。 旋转锁对您没有帮助,因为如果当前执行的任何线程已锁定ConcurrencyDictionary或Spinner变量,则在FinalizerWorker完成之前,它不会释放它,因为它将永远旋转/锁定

您在这里的主要选项是使用SuppressFinalize(this)调用实现IDisposable接口,因为您的对象将抑制终结器工作进程,因此不会发生死锁,并发对话操作是安全的

因此,如果您使用object.Dispose()抢占终结器,您应该可以安全地使用ConcurrencyDictionary,否则,不要在终结器Dispose(false)调用中使用任何类型的锁,否则您将在某个时刻死锁

// Design pattern for a base class.
public class Base: IDisposable
{
    private bool disposed = false;
//Implement IDisposable.
public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
    if (!disposed)
    {
        //Disposing outside the FinalizerWorker (SAFE)
        if (disposing)            
            m_pDictionary.TryRemove(this);            

        disposed = true;
    }
}

// Use C# destructor syntax for finalization code.
~Base()
{
    // Simply call Dispose(false).
    Dispose (false);
}

您可以在此处看到ConcurrentDictionary的实现,TryRemove实现使用“lock(…)”


您可以在析构函数中使用线程池从字典中删除该项。您仍然需要将包装器实例标记为不再有效,这样,如果在运行终结器和删除它的线程池之间调用了它的任何公共方法,您就可以检测到这一点并拒绝该调用。

这里的答案将使您误入歧途。不鼓励在析构函数/终结器中使用锁,因为它很容易导致死锁,特别是在“手动”实现而不是使用并发集合时,但有时这是必要的。即使在“停止世界”GC实现中,终结器也运行在一个单独的终结器线程上,该线程与应用程序同时运行

不过,第一件事是第一件事——你所建议的是实现你想要的功能的理想方式,这是非常罕见的,我很有信心它不是。首先,weakreference不适合在缓存中使用,因为它们的收集频率远远高于“需要内存”时的收集频率。适当的缓存实现使用强引用监控内存级别,并在内存使用率过高时根据需要释放它们

即使在WeakValueDictionary这样的实现中,如果可以收集,您也不希望集合保留该值,但该实现仍然不会接收对象收集通知。相反,只要在偶然发现一个已收集的条目时删除该条目,或者每X次操作或每Y秒扫描整个集合以查找无效条目,等等

也就是说,如果您确实遇到在收集对象时需要执行某些操作的情况,那么可以很好地使用并发收集

假设您不做任何愚蠢的事情,在通知队列中排队ID或从并发字典中删除项目是安全的,因为这些操作速度快,阻塞时间很短,并且在终结器运行时应用程序不会被阻塞


这是你应该尽可能避免的事情,但有时这是实现某些事情的最好也是唯一的方法。只需确保锁是快速的,并尽可能少地使用,而不是任何容易意外出错和死锁的多级锁定方案的一部分。

阅读问题,问题是
我可以在析构函数中安全地使用ConcurrentDictionary.TryRemove吗?
。如果它可以在析构函数中抛出一个不允许的异常,为什么还要尝试呢。在析构函数中清理外部资源通常是不好的。我可以在那里呆一段时间reason@GaryKaizer你知道MSDN推荐的<代码> IDsPiabase<代码>实现模式在析构函数中调用<代码>处置<代码>吗?你说的对,我有点太习惯于管理C++了,这里有区别,将修改我的代码,使其只包含一个检查bDisposing的一次性I。我认为您在这里混合了两种不同的东西:a)FinalizerWorker线程;b) 垃圾收集器线程。其中FinalizerWorker与任何其他线程没有太大区别,而GC线程可能“阻止世界”,因此锁定技术可能导致死锁。幸运的是,您不能在GC线程中实现任何逻辑,因为它完全是由运行时实现的。谢谢,我很担心。你能推荐一些工作吗?可以使用
Interlocked
设置需要清除集合的标志,然后在每个公共方法中为标志设置
CompareExchange
,但这不是非常干净的解决方案。我可以使用一些愚蠢的方法,比如在析构函数中启动一个线程,在GC完成后清理集合吗?我不太清楚