我无法正确处理包含非托管对象的ConcurrentBag
的Dispose/Finalization。运行下面的代码(通常)会在TryTake()
的呼叫中生成ObjectDisposedException
(Cannot access a disposed object.Object name: 'The ThreadLocal object has been disposed.'.
)。包含非托管对象的ConcurrentBag的终结
大概在这种情况下,垃圾收集器在调用A的终结器之前破坏了ConcurrentBag
。我以为只有在ConcurrentBag
本身实现了终结器的情况下才会这样。是否在终止路径期间不应该触摸托管对象?
class A : IDisposable
{
private readonly ConcurrentBag<object> _collection = new ConcurrentBag<object>();
public A(string value)
{
if (value == null) throw new ArgumentNullException();
}
~A()
{
Dispose(false);
}
public void Dispose() => Dispose(true);
private void Dispose(bool disposing)
{
if (disposing) {}
object value;
while (_collection.TryTake(out value))
{
// Cleanup value
}
}
}
触发异常:
void Main()
{
var a = new A(null);
}
下似乎解决这一具体问题,但我不能确定这是否是安全。这种情况下是否有完全安全的实现?
while (_collection.IsEmpty == false)
{
object value;
_collection.TryTake(out value);
// Cleanup value
}
你的解释很有道理。 基于Stephen Cleary的文章,正确的实现是将非托管对象包装在IDisposable包装中(他称为“级别0”),该包装处理考虑完成的肮脏工作。这允许更高级别的我的类A(即级别1)类只关心处理路径并忽略最终化路径。 – Terrence
唯一不正确的是0级对象应该从'SafeHandle'派生出来,而不是'IDisposeable',但除此之外,是的。 –