【问题标题】:Guarantee that Dispose is called "when the process is ending"保证在“进程结束时”调用 Dispose
【发布时间】:2016-02-11 06:05:37
【问题描述】:

有一个可能共享的资源,X与前台线程关联,它被两个实例 (Y, Z) 使用并作为依赖项提供。因为 X 可以被共享,所以它不能被任何一个使用站点(Y 或 Z)处理掉。

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

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

有没有一种方法可以让 X - 无需外部依赖,甚至知道它是否在“可执行文件”或“Windows 服务”中 - 确保在进程终止之前/之后调用 Dispose?

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

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

【问题讨论】:

  • 对您来说,“过程即将结束”是什么意思?除了正在运行的 X 之外的所有前台线程的终止?或者您是否有更明确的信号,例如引发的事件或设置为 false 的标志,它打破了 while 循环?
  • 你能得到流程实例吗?并连接到 Process Exited 事件?
  • @Viru 该事件不会触发,因为来自X 的线程使进程保持活动状态。
  • 另一个选项类似于'让每个 Y/Z 都处理 X,除非指示不要这样做' .. 或者至少将错误推到其他地方,这比拥有更好(在这种情况下)一个无法可靠终止的进程。
  • 我想我可能有一个 hacky 解决方案可以做到这一点,它基本上是您的终结器解决方案,但不是终结器,而是使用计时器检查 WeakRefrences 列表。我现在没有时间,但明天早上我会用代码示例写一个正式的答案。编辑:当然最好的解决方案是“修复错误的客户端并停止尝试解决问题”,但这是现实世界。

标签: c# .net multithreading dispose shutdown


【解决方案1】:

这不会在 X 上调用 dispose,但它会告诉您何时所有 X 已被 GC 收集(并且每 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 为假时,已经在T 上调用了终结器。

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

【讨论】:

  • 这回答了这个问题,虽然我没有尝试过实践~我们决定只是解决其他地方设计的代码的问题并更改合同,使其无效从任何容器内部处置共享资源,并且在不手动处置(现在始终是外部的)资源的情况下结束进程是无效。令人讨厌的清理工作,但现在已经完成了:}
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-27
  • 2018-11-19
  • 2010-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多