首页天道酬勤C# System.Threading.Timer 详解及示例

C# System.Threading.Timer 详解及示例

admin 02-25 10:58 32次浏览

阅读目录


前言

一、两类重载

1、 Timer(TimerCallback)

2、Timer(TimerCallback, Object, Int32, Int32)

二、属性 ActiveCount

三、方法

1、Timer.Change 方法

2、Timer.Dispose 方法

3、Timer.DisposeAsync 方法

 

回到顶部

前言

定时器功能在日常开发中也很常用.Net实际上有五种定时器,分别是:System.Timers.Timer、System.Threading.Timer、System.Windows.Forms.Timer、System.Web.UI.Timer(仅.NETFramework)、System.Windows.Threading.DispatcherTimer。

Systemm是最常用的一种.Threading.与其他几种定时器相比,Timer基于线程池的定时器具有更高的安全性和最强的适用性。因此,本文将详细介绍该定时器的相关内容。

回到顶部

一、两种重载

参考:Timer构造函数

1、Timer(TimerCallback)

以新创建的timer对象为状态对象,以无限周期和无限到期时间初始化timer的新例子。当循环任务完成时,当前的timer对象可以在回调函数中释放。

//语法

publicTimer(System.Threading.TimerCallbackcallback);

以下是一个简单的例子:(在回调函数TimerProc中,我们可以释放Timer对象来结束循环过程)

using System;

using System.Threading;


namespace Test.Test.ConsoleApp

{

    class Program

    {

        static void Main(string[] args)

        {

            Program ex = new Program();

            ex.StartTimer(4000); // 创建两个 Timer 对象

            ex.StartTimer(1000);

            Console.WriteLine("Press Enter to end the program.");

            Console.ReadLine();

        }

        public void StartTimer(int dueTime)

        {

            Timer t = new Timer(new TimerCallback(TimerProc));

            t.Change(dueTime, 5000); // 启动定时器

            // dueTime:表示延迟调用的时间;

            // 5000:表示回调的时间间隔,单位:毫秒

            // 如果在回调中将 Timer 释放掉,则后续回调将无法发生

        }

        private void TimerProc(object state) // 入参对象为 Timer 对象

        {

            Timer t = (Timer)state;

            t.Dispose(); // 调用一次就释放掉,或者添加条件释放

            Console.WriteLine("The timer callback executes.");

        }

    }

}

// 输出结果:

// Press Enter to end the program.

// The timer callback executes.

// The timer callback executes.

2、Timer(TimerCallback, Object, Int32, Int32)

使用 32 位的有符号整数指定时间间隔,初始化 Timer 类的新实例。callback:回调函数名;state:包含回调方法要使用的信息的状态对象,可为空;dueTime:延迟调用的时间;period:重复回调的时间间隔。


// 语法

public Timer (System.Threading.TimerCallback callback, object? state, int dueTime, int period);

下面是一个示例:(关于线程自动重置类:AutoResetEvent 类)


通过线程同步事件,演示不同时间间隔输出结果的区别

与此重载类似用法的另外三个重载如下:


// 1、用 64 位整数表示时间间隔

Timer(TimerCallback, Object, Int64, Int64)

// 2、时间戳参数

//  时间戳语法:public TimeSpan (int hours, int minutes, int seconds);

//  TimeSpan delayTime = new TimeSpan(0, 0, 1); 

//  时间戳语法:public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds, int microseconds);

//  TimeSpan intervalTime = new TimeSpan(0, 0, 0, 0, 250);

Timer(TimerCallback, Object, TimeSpan, TimeSpan)

// 3、用 32 位无符号整数来表示时间间隔

Timer(TimerCallback, Object, UInt32, UInt32)

回到顶部

二、属性 ActiveCount

获取当前活动的计时器数量。 活动计数器定义为,在未来某一时间点触发且尚未取消。


下面是一个简单的示例:


using System;

using System.Threading;


class Program

{

    static void Main()

    {

        var stateTimer = new Timer((para)=>{ }, null, 1000, 250);

        var stateTimer2 = new Timer((para) => { }, null, 1000, 250);

        Console.WriteLine($"1、Timer ActiveCount.{Timer.ActiveCount}");

        stateTimer.Dispose(); // 释放 Timer 对象

        Console.WriteLine($"\n2、Timer ActiveCount.{Timer.ActiveCount}");

        stateTimer2.Dispose(); // 释放 Timer2 对象

        Console.WriteLine($"\n3、Timer ActiveCount.{Timer.ActiveCount}");

        Thread.Sleep(5000);

    }

}

// 输出结果:

// 1、Timer ActiveCount.2

// 

// 2、Timer ActiveCount.1

// 

// 3、Timer ActiveCount.0

回到顶部

三、方法

1、Timer.Change 方法

更改计时器的延迟启动时间和方法循环调用之间的时间间隔。单位均为毫秒(ms)。


其和 Timer 的构造函数重载类似,都是有四个重载,之间只有参数不同,用法相同。


四种类型分别是:Int32(32 位正整数)、Int64(64 位正整数)、TimeSpan(时间戳)、UInt32(32 位无符号整数)。


下面例举一个时间为正整数的示例:


// 先创建一个 Timer 对象

var stateTimer = new Timer((para)=>{ }, null, 1000, 250);

// 调用变更对象的方法如下:1000 表示:延迟 1s 触发;500 表示间隔 0.5s 循环调用

stateTimer.Change(1000, 500);

2、Timer.Dispose 方法

此方法共有两个重载,分别是:Dispose()、Dispose(WaitHandle)。


Dispose()

// 释放由 Timer 实例使用的当前所有资源

Dispose(WaitHandle)

// 释放由 Timer 实例使用的当前所有资源,并在释放完成时发出信号

Dispose() 方法就是直接将 Timer 对象释放调,这里就不再赘述了,下面来看一个关于 Dispose(WaitHandle) 的示例:


using System;

using System.Threading;


namespace TimerDispose

{

    class Program

    {

        static Timer timer = null; //**声明一个全局变量,避免 Timer 对象后续没有调用时,被 GC回收

        // ManualResetEvent 继承自 WaitHandle

        // 是否手动重置事件(是 WaitHandle 的子类) false 初始状态为无信号 true 初始状态为有信号

        static ManualResetEvent timerDisposed = null;

        static void CreateAndStartTimer()

        {

            // 初始化 Timer,设置触发间隔为 2000 毫秒,设置 dueTime 参数为 Timeout.Infinite 表示不启动 Timer

            timer = new Timer(TimerCallBack, null, Timeout.Infinite, 2000);

            // 启动 Timer,设置 dueTime 参数为 0 表示立刻启动 Timer

            //**先实例化再启动的目的是:避免在调用 Dispose 方法前,timer 对象还未完成赋值,所导致的空对象错误

            timer.Change(0, 2000);

        }

        /// <summary>

        /// TimerCallBack 方法是 Timer 每一次触发后的事件处理方法

        /// </summary>

        static void TimerCallBack(object state)

        {

            // Thread.Sleep(10000); // Change() 报错:System.ObjectDisposedException: 'Cannot access a disposed object.'

            try

            {

                timer.Change(0, 1000);

            }

            catch (ObjectDisposedException) //**当 Timer 对象已经调用了 Dispose 方法后,再调用 Change 方法,会抛出 ObjectDisposedException 异常

            {

                Console.WriteLine("在 Timer.Dispose 方法执行后,再调用 Timer.Change 方法已经没有意义");

            }

            Thread.Sleep(10000); // 在 Change() 之后可正常运行

        }

        static void Main(string[] args)

        {

            CreateAndStartTimer();

            Console.WriteLine("按任意键调用Timer.Dispose方法...");

            Console.ReadKey();

            timerDisposed = new ManualResetEvent(false);

            // 调用 Timer 的 bool Dispose(WaitHandle notifyObject) 重载方法,来结束Timer的触发,

            // 当线程池中的所有 TimerCallBack 方法都执行完毕后,Timer 会发一个信号给 timerDisposed

            timer.Dispose(timerDisposed);

            // WaitHandle.WaitOne() 方法会等待收到一个信号,否则一直被阻塞

            timerDisposed.WaitOne();

            timerDisposed.Dispose();

            Console.WriteLine("Timer已经结束,按任意键结束整个程序...");

            Console.ReadKey();

        }

    }

}

另一个很好的例子,是一位外国大师写的,不仅考虑到提示.Change方法会抛出Objectdisposedexception异常,他还给Waithandle.Waitone方法增加了超时限制(_disposalTimeout),还增加了逻辑来防止Timerr.多次重复调用Dispose方法,注意Timer的booldispose(WaitHandlenotifyObject)重载方法是返回bool值,如果它返回false,则表示timerr.Dispose方法已被调用,代码如下:

更好的示例代码

参考:System.Threading.Disposer如何正确地被Disposer使用?

3、Timer.Disposeasync方法

它是上一部分的异步实现。

从.从NETCore开始,就意味着.NET已经进入了一个新的异步时代。无论是各种基础类库(如Systemm).IO)、AspNetCore、或者EFCore等等,他们都逐渐支持异步操作。它不会阻止线程的执行,带来高性能,基本上不需要改变原来的编码习惯,所以后续的异步编程肯定会越来越普遍。

当实体类同时实现Dispose和Disposeasync时,由于程序在先判断时实现了Disposeasync的异步释放,因此一般优先考虑异步释放。参考:熟悉又陌生的新朋友——IAsyncDisposable


JavaScript 中的 apply、call、bind 澳洲幸运10官网开奖结果号码_全网最准澳洲幸运10计划软件