保证调用 Dispose "when the process is ending"
Guarantee that Dispose is called "when the process is ending"
有一个可能共享的资源,X 与前台线程相关联,由两个实例(Y、Z)使用并作为依赖项提供。因为 X 可能会被共享,所以它不能被任何一个使用站点(Y 或 Z)丢弃。
显而易见的解决方案是确保 X 在不再使用后 手动 处置 'at some appropriate time':在这种情况下,可以假设这意味着 X不再是强可达的。但是,在这种情况下,有一个 [有问题的] 客户端不会这样做。
由于 X 有一个关联的前台线程,这可以防止进程终止(完全?)。
有没有办法让 X - 没有外部依赖性,甚至不知道它是否在 'executable' 或 'Windows Service' 中 - 确保在 before/as 过程中调用 Dispose正在终止?
是否有更智能的方法来处理这种可能有一个或多个消费者的(未跟踪的)共享资源?
目前这依赖于终结器,但正如所宣传的那样.. 不可靠。
这不会在 X 上调用处置,但它会告诉您何时 GC 收集了所有 X(并且每 5 分钟强制执行一次 GC)
public sealed class CollectionWatcher<T>
{
private readonly Func<T> _objectFactory;
private readonly List<WeakReference> _references;
private readonly System.Timers.Timer _timer;
private readonly Object _lockObject = new Object();
public CollectionWatcher(Func<T> objectFactory, double minutesBetweenChecks = 5)
{
_objectFactory = objectFactory;
_references = new List<WeakReference>();
_timer = new System.Timers.Timer();
_timer.Interval = TimeSpan.FromMinutes(minutesBetweenChecks).TotalMilliseconds;
_timer.AutoReset = true;
_timer.Elapsed += TimerElapsed;
}
public T GetObjectBecauseICantBeTrustedToDisposeCorrectly()
{
var result = _objectFactory();
var wr = new WeakReference(result);
lock (_lockObject)
{
_references.Add(wr);
_timer.Start();
}
return result;
}
public event EventHandler AllItemsCollected;
private void OnAllItemsCollected()
{
AllItemsCollected?.Invoke(this, EventArgs.Empty);
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
bool allItemCollected = false;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, false);
lock (_lockObject)
{
foreach (var reference in _references.Where(x => !x.IsAlive).ToList())
{
_references.Remove(reference);
}
if (_references.Count == 0)
{
_timer.Stop();
allItemCollected = true;
}
}
if (allItemCollected)
{
OnAllItemsCollected();
}
}
}
您不能只在 T
上调用 Dispose()
,因为到 .IsAlive
为 false 时终结器已经在 T
上调用。
我会继续思考这个问题,也许还有其他解决方案。
有一个可能共享的资源,X 与前台线程相关联,由两个实例(Y、Z)使用并作为依赖项提供。因为 X 可能会被共享,所以它不能被任何一个使用站点(Y 或 Z)丢弃。
显而易见的解决方案是确保 X 在不再使用后 手动 处置 'at some appropriate time':在这种情况下,可以假设这意味着 X不再是强可达的。但是,在这种情况下,有一个 [有问题的] 客户端不会这样做。
由于 X 有一个关联的前台线程,这可以防止进程终止(完全?)。
有没有办法让 X - 没有外部依赖性,甚至不知道它是否在 'executable' 或 'Windows Service' 中 - 确保在 before/as 过程中调用 Dispose正在终止?
是否有更智能的方法来处理这种可能有一个或多个消费者的(未跟踪的)共享资源?
目前这依赖于终结器,但正如所宣传的那样.. 不可靠。
这不会在 X 上调用处置,但它会告诉您何时 GC 收集了所有 X(并且每 5 分钟强制执行一次 GC)
public sealed class CollectionWatcher<T>
{
private readonly Func<T> _objectFactory;
private readonly List<WeakReference> _references;
private readonly System.Timers.Timer _timer;
private readonly Object _lockObject = new Object();
public CollectionWatcher(Func<T> objectFactory, double minutesBetweenChecks = 5)
{
_objectFactory = objectFactory;
_references = new List<WeakReference>();
_timer = new System.Timers.Timer();
_timer.Interval = TimeSpan.FromMinutes(minutesBetweenChecks).TotalMilliseconds;
_timer.AutoReset = true;
_timer.Elapsed += TimerElapsed;
}
public T GetObjectBecauseICantBeTrustedToDisposeCorrectly()
{
var result = _objectFactory();
var wr = new WeakReference(result);
lock (_lockObject)
{
_references.Add(wr);
_timer.Start();
}
return result;
}
public event EventHandler AllItemsCollected;
private void OnAllItemsCollected()
{
AllItemsCollected?.Invoke(this, EventArgs.Empty);
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
bool allItemCollected = false;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, false);
lock (_lockObject)
{
foreach (var reference in _references.Where(x => !x.IsAlive).ToList())
{
_references.Remove(reference);
}
if (_references.Count == 0)
{
_timer.Stop();
allItemCollected = true;
}
}
if (allItemCollected)
{
OnAllItemsCollected();
}
}
}
您不能只在 T
上调用 Dispose()
,因为到 .IsAlive
为 false 时终结器已经在 T
上调用。
我会继续思考这个问题,也许还有其他解决方案。