C#中 System.Threading.Timer 的回收问题

时间:2020-02-26 12:55:04   收藏:0   阅读:165

一. 问题来源

在我上家公司里,做停车软件客户端的时候,岗亭客户端需要每隔一段时间,将本地时间和服务所在的电脑上的时间,和中央服务器上的本地时间进行同步。但是在实际运用的时候,打开客户端除了开启计时器(System.Threading.Timer)的时候会同步一次以外,之后就再也不会同步。

二. 关于 System.Threading.Timer

System.Threading.Timer 是一个比较特殊的对象,在程序还没有执行到离开 System.Threading.Timer 的作用域的时候。如果发生一次 GC 的回收,那么在 Release 编译的模式下,这个计时器会直接被当做垃圾而被 CLR 回收,造成无法正常进行定时操作。

当然在 Debug 编译模式下,并不会发生这个问题。因为在 Debug 模式下,编译器会添加相关的方法特性(编译器会为程序集设置 DebuggingModes 的 DisableOptimizations 标志)来阻止 CLR 垃圾回收器在离开作用域之前回收它,将所有根的生存周期延长至方法结束。

值得一提的是,只要有一个根在引用它,对于其他的对象,并不会造成在离开作用域之前就被回收。所以 System.Threading.Timer 需要我们在某些特殊情况下区别于其他对象进行对待。

三. 解决 System.Threading.Timer 被提前回收的方法

解决这个被提前回收的方法主要有一下几种:

1. 如果是在命令窗口中编译,使用 " /Debug+ " ,于此同时不要去启用 " /optimize "。如果是在VS中那么直接在上面的工具栏中选择 " Debug " 即可。当然这种方法是有缺点的,就是不能处理 Release 编译的程序;

操作步骤:

step 1:测试代码(已经在应用进行精简删除 仅仅保留命名空间 using System )

?using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Threading.Timer timer = new System.Threading.Timer(ShowCuurentDataTime, null, 0, 1000);

            //让timer不能离开当前作用域
            Console.ReadKey();
        }

        public static void ShowCuurentDataTime(Object obj)
        {
            Console.WriteLine("[System.Threading.Timer]当前时间:" + DateTime.Now);

            //强制GC回收
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        }
    }
}

step 2:命令窗口中编译

?技术分享图片

csc /t:exe /r:System.dll /Debug+ /optimize- program.cs

如下图:

?技术分享图片

step 3:如果是在 Visual Studio IDE 下那么直接在工具栏上把 Debug 选上编译运行,在 bin\debug 目录下的会有 exe ,如下图:

?技术分享图片

注:

timer.Dispose();

值得注意的是,并不能使用

t=null;

这样来引用,因为编辑器会优化代码,而忽略这一行。还有一种方式就是把 Timer 设置为 Timer 承载对象的一个字段或者属性,让 Timer 的生命周期得以延长到承载对象的生命周期结束:

【1】在程序退出之前,对计时器进行显式的引用释放的方法,如下图:

?技术分享图片

【2】使用字段或者属性,延长Timer生命周期,如下图:

?技术分享图片 

四. 项目工程下载

下载地址

原文:https://www.cnblogs.com/Jeffrey-Chou/p/12366185.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!