【问题标题】:How to prevent my Timer from GC collecting before its callback excuted?如何防止我的 Timer 在执行回调之前收集 GC?
【发布时间】:2009-03-04 12:52:07
【问题描述】:

我需要创建一堆计时器作为局部变量来执行以下操作:

void Foo()
{
    Timer t = new Timer(myTimerCallback,null,1000,Timeout.Infinite);
}

不幸的是,其中一些在 1 秒后调用 myTimerCallback 之前被 GC 收集。由于我有这么多定时器,不可能将它们存储在私有静态成员中。而且也很难找到放置 GC.Keeplive(t) 的地方。

我怎样才能让每个计时器在死前做自己的事情?

【问题讨论】:

  • 我不想将这些计时器存储在私人成员中的原因是太多了。我可以试试,但有没有更好的方法?
  • 如果要存储的计时器太多,也许是时候退后一步,问问自己是否解决了正确的问题?
  • 附议@damien 的评论。您不应该在托管运行时对抗 GC。我建议咬紧牙关并存储参考资料。您可能会惊讶于它对性能的影响如此之小。
  • @Renze-de-waal、@Sean、@hans-passant、@okutane 我今天了解到public Timer(TimerCallback callback) 使用this 代替state,并且可以在垃圾收集中存活。

标签: c#


【解决方案1】:

最好的办法是将它们存储在成员变量中,然后在不再需要它们时丢弃它们。

【讨论】:

    【解决方案2】:

    您可以将它们存储在集合中以保留引用,并在事件方法中从集合中删除引用。我认为您需要使用 state 参数来识别集合中的计时器(传递对计时器的引用或集合中的键作为“新计时器”语句中的状态参数)。

    请注意,回调函数在其他线程上执行,因此您将/可能有多个回调函数同时运行。使用锁定来确保添加/删除引用是安全的。

    【讨论】:

      【解决方案3】:

      将它们存储在成员中,例如List<Timer>

      【讨论】:

        【解决方案4】:

        为什么无法存储它们?也许您可以在他们的工作完成后将它们从该集合中删除?

        【讨论】:

          【解决方案5】:

          重要的是看到 GC.KeepAlive() 不能解决您的问题。这只是将对象的生命周期延长到语句。只要计时器需要保持活动状态,您就必须循环或阻塞。另请注意,局部变量中引用的生命周期在 Debug 构建中是不同的。 JIT 使它们保持生命,直到方法结束以允许手表工作。产生棘手的“在调试中有效,在发布模式下无效”的问题。

          您必须保留对计时器对象的生命引用。这通常是通过类中的一个字段来完成的。生命周期要求现在传递给类对象,只要需要计时器,它就需要保持活动状态。通常不是问题。如果是,则必须将引用设为静态。

          【讨论】:

            【解决方案6】:

            A. System.Threading vs System.Timers

            值得指出的是,保留引用问题是特定于 System.Threading.Timer 而不是 System.Timers.Timer


            B. Threading.Timer的Timer(TimerCallback callback)构造函数

            这不是您问题的解决方案,但我认为它仍然与主题相关。

            System.Threading.TimerTimer(TimerCallback callback) 构造函数(不使用dueTime 和其他构造函数的构造函数)使用this 代替state,这意味着计时器将保留对自身的引用,这意味着它将在垃圾收集中幸存下来。

            public Timer(TimerCallback callback)
            {
                (...)
                TimerSetup(callback, this, (UInt32)dueTime, (UInt32)period, ref stackMark);
            }
            

            示例

            计时器“C”是使用Timer(TimerCallback callback) 创建的,它一直在GC.Collect() 之后运行。

            输出

            A
            B
            C
            A
            B
            C
            GC Collected
            B
            C
            C
            B
            B
            C
            

            代码

            class TimerExperiment
            {
                System.Threading.Timer timerB;
            
                public TimerExperiment()
                {
                    StartTimer("A"); // Not keeping this timer
                    timerB = StartTimer("B"); // Keeping this timer
                    StartTimer2("C"); // Not keeping this timer
                }
            
                static System.Threading.Timer StartTimer(string name) {
                    return new System.Threading.Timer(_ =>
                    {
                        Console.WriteLine($"{name}");
                    }, null, dueTime: Delay(name), period: TimeSpan.FromSeconds(1));
                }
            
                static System.Threading.Timer StartTimer2(string name)
                {
                    //Create the timer using the constructor which only takes the callback
                    var t = new System.Threading.Timer( _ => Console.WriteLine($"{name}"));
                    t.Change(dueTime: Delay(name), period: TimeSpan.FromSeconds(1));
                    return t;
                }
            
                static TimeSpan Delay(string name) 
                        => TimeSpan.FromMilliseconds(Convert.ToInt64(name[0])*10);
            
            }
            
            class Program
            {
                static async Task Main(string[] args)
                {
                    var withTimers = new TimerExperiment();
            
                    await Task.Delay(TimeSpan.FromSeconds(2));
                    GC.Collect();
                    Console.WriteLine("GC Collected");
                    Console.ReadLine();
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-04-11
              • 1970-01-01
              • 1970-01-01
              • 2021-01-18
              • 1970-01-01
              • 1970-01-01
              • 2019-07-24
              • 2019-04-09
              相关资源
              最近更新 更多