如何解决C#中IDisposable对象循环Dispose的栈溢出问题
如何避免循环依赖的IDisposable对象释放时出现无限递归?
当两个存在循环依赖的IDisposable对象需要互相传播释放操作时,如何避免Dispose()方法因无限递归引发StackOverflowException?
我初步设想通过标记对象是否正在释放来提前退出,并实现了如下示例代码:
using CircularDispose; using JetBrains.Annotations; Example a = new(nameof(a)); Example b = new(nameof(b)); a.Other = b; b.Other = a; Console.WriteLine("Start"); a.Dispose(); Console.WriteLine($"{nameof(a)} is disposed: {a.IsDisposed}"); Console.WriteLine($"{nameof(b)} is disposed: {b.IsDisposed}"); Console.WriteLine("Finished"); namespace CircularDispose { [PublicAPI] public class Example : IDisposable { private bool _isDisposing; private string _name; public Example? Other { get; set; } public bool IsDisposed { get; private set; } public Example(string name) => _name = name; public void Dispose() { GC.SuppressFinalize(this); Dispose(true); } private void Dispose(bool disposing) { Console.WriteLine($"{_name}.{nameof(Dispose)}({disposing}) was called"); if (!_isDisposing && !IsDisposed) { _isDisposing = true; if (disposing) { DisposeManagedResources(); } DisposeUnmanagedResources(); IsDisposed = true; } } ~Example() { Dispose(false); } [PublicAPI] protected virtual void DisposeUnmanagedResources() { // dispose unmanaged resources } [PublicAPI] protected virtual void DisposeManagedResources() { Other?.Dispose(); Other = null; } } }
程序输出为:
Start a.Dispose(True) was called b.Dispose(True) was called a.Dispose(True) was called a is disposed: True b is disposed: True Finished
但我未在快速搜索中找到同类实现,因此想确认该方案是否安全可行。
你的方案是安全可行的,核心思路通过_isDisposing标记避免重复进入释放逻辑,完美解决了循环依赖下的递归问题。
具体逻辑分析:
- 调用
a.Dispose()后,a的_isDisposing被设为true,随后触发b.Dispose(); b进入释放流程后同样标记自身_isDisposing为true,再调用a.Dispose();- 此时
a的_isDisposing已为true,直接跳过核心释放逻辑,终止了递归循环; - 最终两个对象都会完成完整的释放流程,
IsDisposed均被正确设置为true。
额外优化建议:
- 如果你的对象会在多线程环境中被调用释放,建议给
_isDisposing和IsDisposed添加volatile修饰,确保线程间的可见性; - 当前实现中
DisposeManagedResources最后将Other设为null的操作很合理,能帮助GC更快识别可回收对象,减少内存占用。
内容的提问来源于stack exchange,提问作者michi-b
相关产品推荐
相关产品推荐

