c# 线程定时器 System.Threading.Timer的使用
作者:驰愿 发布时间:2022-07-08 01:28:09
System.Threading.Timer 是由线程池调用的。
所有的Timer对象只使用了一个线程来管理。这个线程知道下一个Timer对象在什么时候到期。下一个Timer对象到期时,线程就会唤醒,在内部调用ThreadPool 的 QueueUserWorkItem,将一个工作项添加到线程池队列中,使你的回调方法得到调用。如果回调方法的执行时间很长,计时器可能(在上个回调还没有完成的时候)再次触发。这可能造成多个线程池线程同时执行你的回调方法。
参数
callback : 一个Object 类型参数的委托,周期调用的函数。
state: callback 委托调用时的参数。
dueTime: 定时器延时多久开始调用。单位 毫秒
period: 定时器每隔多久调用一次。单位 毫秒
不能使用局部变量来创建指向一个线程定时器。因为局部变量会被GC回收,导致定时器失效。
比如下面的例子:
static void Main(string[] args) {
Run();
//为了加快GC垃圾回收,不停的创建对象
for (int i = 0; i < 10000; i++) {
cc tmp = new cc();
Thread.Sleep(10);
}
Console.ReadKey();
}
static int id;
static void Run() {
Timer timer = new Timer(DoTime, null, 500, 500);
}
static void DoTime(object obj) {
Console.WriteLine(id++);
}
class cc{
public int[] a = new int[1000];
}
定时器执行4次后停止了。定时器什么时候停止取决于GC什么时候回收。如果一直没有GC的回收,那么将会一直执行下去。比如把上方创建 cc 对象的代码删除。定时器将会一直执行。
可以在Main方法中使局部变量 或者 使用全局变量来存放Timer 对象 避免 clr 把Timer 回收。
比如改成这样
static Timer timer;
static void Main(string[] args) {
timer = new Timer(DoTime, null, 500, 500);
//为了加开GC垃圾回收,不停的创建对象
for (int i = 0; i < 10000; i++) {
cc tmp = new cc();
Thread.Sleep(10);
}
Console.ReadKey();
}
static int id;
static void DoTime(object obj) {
Console.WriteLine(id++);
}
如果回调方法的执行时间很长,计时器可能(在上个回调还没有完成的时候)再次触发。这可能造成多个线程池线程同时执行你的回调方法。
static Timer timer1;
static void Main(string[] args) {
timer1 = new Timer(DoTime, 1, 500, 500);
Console.ReadKey();
}
static int id;
static void DoTime(object obj) {
int idx = id ++;
Console.WriteLine("处理开始:" + idx + "," + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1200);
Console.WriteLine("处理完毕:" + idx + "," + Thread.CurrentThread.ManagedThreadId);
}
定时器第一次任务还没执行完毕,第二次,第三次回调就开始了。多个线程同时并发 DoTime 方法
解决方法
period 使用 Timeout.Infinite。这个参数将导致DoTime 只处理一次。
在回调方法中使用 Change方法来修改 dueTime,period 参数。period 继续使用 Timeout.Infinite. 使用这个方法要注意如果timer 在被Dispose了,使用Change 将会引发异常。
比如
static Timer timer1;
static void Main(string[] args) {
timer1 = new Timer(DoTime, 1, 0, Timeout.Infinite);
Console.ReadKey();
}
static int id;
static void DoTime(object obj) {
int idx = id ++;
Console.WriteLine("处理开始:" + idx + "," + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1200);
Console.WriteLine("处理完毕:" + idx + "," + Thread.CurrentThread.ManagedThreadId);
timer1.Change(500,Timeout.Infinite);
}
使用Disponse停止定时器
如果Timer 不会再使用 则可以 使用 Dispose 方法来停止定时器。
如果定时器运行到中途。使用Dispose方法后,callback还是会执行完一个完整的生命周期,不会中途停止。并且Dispose方法不会等待 callback的这次调用完成。只是定时器下次不再调用 callback。
使用Change停止定时器
把 dueTime 参数置为-1就可以停止定时器。同样的,它不会中断在运行中的callback,只是下一次不再回调。 这个方法停止的定时器 还可以使用Change 再次利用定时器。
来源:https://blog.csdn.net/qq_27461747/article/details/105505420
猜你喜欢
- 简介Exchanger是一个用于线程间数据交换的工具类,它提供一个公共点,在这个公共点,两个线程可以交换彼此的数据。当一个线程调用excha
- 1 简介IDEA的全称是IntelliJ IDEA,这是一个java编程语言开发的集成环境。IDEA的每一个方面都是为了最大限度地提高开发人
- 前一段时间粗略看了一下《深入Java虚拟机 第二版》,可能是因为工作才一年的原因吧,看着十分的吃力。毕竟如果具体到细节的话,Java虚拟机涉
- 前言经过前面《Unity3D入门教程》系列讲解,再加上我们自己的探索,相信大家已经掌握了Unity3D的相关知识和基本方法。本文将使用前面学
- 类的结构包括 :1. 成员变量2. 成员方法3. 构造方法4. 代码块5. 内部类第一 构造方法的作用主要有以下三方面的作用:(1)在构造方
- 本文实例为大家分享了Android实现随手指移动小球的具体代码,供大家参考,具体内容如下这个随手指移动小球,首先要使用paint画笔在can
- 目录进程、线程1. 进程2. 线程分时、分片同步、异步异步、多线程异步多线程效率多线程无序性扩展异步多线程版本下一篇:C# 异步多线程入门到
- 目录引言命名规则代码排版1.代码缩进对齐2.遇到分号换行3.大括号、括号等成对出现4.加上注释Java注释注释的作用注释的3种类型给代码加上
- 本文为大家分享了java组件实现文件上传功能的具体代码,供大家参考,具体内容如下1 SmartUpload上传组件SmartUpload上传
- 前言接下来是 Spring Boot 统⼀功能处理模块了,也是 AOP 的实战环节,要实现的课程⽬标有以下 3 个:统⼀⽤户登录权限验证统⼀
- 一、在JAVA开发领域,目前可以通过以下几种方式进行定时任务1、单机部署模式Timer:jdk中自带的一个定时调度类,可以简单的实现按某一频
- 在 C# 以二进制形式读取数据时使用的是 BinaryReader 类。BinaryReader 类中提供的构造方法有 3 种,具体的语法形
- 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档前言这两天在项目中使用到Java的导入导出功能,以前对这块有一定了解,但是没
- 概念装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。装饰者和被装饰对象有相同的超类型。你可以用一个或
- Pull解析方法给应用程序完全的控制文档该怎么样被解析。Android中对Pull方法提供了支持的API,主要是org.xmlpull.v1
- 扫码抢实现读取二维码信息,本地扫码枪是外接写入设备,本质是监控读写输入,下面介绍下扫码设备读取支付二维码。1.引入扫码设备辅助类public
- 近期公司要做报表功能,在网上搜索下表格的样式后便自己写了一个自定义的表格控件,该表格控件能根据设置的数据中数据的最大值自动设置左侧信息栏显示
- 一:前言:最近支付后台登录一段时间后如果没有任何操作,总是需要重新登录才可以继续访问页面,出现这个问题的原因就是session超时,debu
- 这节主要完成一些基本的增删改查以及Service、Dao和Action的抽取。1. Service层的抽取  
- 需求描述 今日需求是删除资源时同时删除与该资源绑定的角色数据,有两张表,资源表、