保证Dispose被称为“当过程结束时”

时间:2016-02-11 06:05:38

标签: c# .net multithreading dispose shutdown

有一个可能共享的资源,X 与前台线程相关联,由两个实例(Y,Z)使用并作为依赖项提供。由于X可能是共享的,因此无法通过使用站点(Y或Z)进行处理。

显而易见的解决方案是确保X在不再使用之后在某个适当的时间“手动处置”:在这种情况下,可以假设这意味着X不再强烈-reachable。但是,在这种情况下,有一个[错误]客户端不会这样做。

由于X有一个关联的前台线程,这可以防止进程终止(完全?)。

有没有办法让X - 没有外部依赖关系,甚至知道这些是否在'可执行'或'Windows服务'中 - 确保在进程终止之前/之前发生对Dispose的调用?

是否有更智能的方法来处理可能有一个或多个消费者的这种(未跟踪的)共享资源?

目前这依赖于终结者,但正如所宣传的那样,这是......不可靠。

1 个答案:

答案 0 :(得分:0)

这不会在X上调用dispose,但它会告诉您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();
        }
    }
}

您不能只在Dispose()上致电T,因为当.IsAlive为假时,已在T上调用终结工具。

我将继续考虑这个问题,也许还有其他解决方案。