1.摘要

本文会介绍一个C#中最简单定时任务的使用方法,以及会遇到的定时任务被阻塞现象,从笔者理解的角度分析原因。以及提供解决方案。

2.C#中定时任务的最简方法

  protected internal void PollClient()
{
int i=0;
Timer t = new Timer(p => {
i++;
if (deviceContextList.Count > 0)
{
var deviceContext=GetDeviceContext("123456789");
SendMessage(messageList[i%7],deviceContext.tcpSession.writerContext);
logger.Info("客户端数量:"+ deviceContextList.Count);
}
else
{
logger.Info("客户端数量为0");
Console.WriteLine("客户端数量为0");
}
}, null, 0, 1000) ;
}

上面的timer方法提供于微软System.Threading命名空间。System.Threading.Timer 是由线程池调用的。所有的Timer对象只使用了一个线程来管理。这个线程知道下一个回调对象在什么时候到期。下一个回调对象到期时,线程就会唤醒,在内部调用ThreadPool 的 QueueUserWorkItem,将一个工作项添加到线程池队列中,使你的回调方法得到调用。此方法有多个重载,具体读者可以自行去看。

Timer(TimerCallback callback, object state, int dueTime, int period)

第一个参数callback是回调方法,第二个参数state可以传参给回调方法的参数,第三个参数dueTime是第一次执行回调函数的延时时间,单位毫秒,第四个参数period是调用回调函数的时间间隔。使用起来是不是特别方便,把你需要执行的定时任务放在回调方法中,可独立写成方法,也可像上面一样写成匿名方法的形式。

3.定时任务阻塞现象

当上述任务被执行了几千次以后,定时任务会阻塞,不再执行,也不再打印日志。并且上面的写法有缺陷,。如果回调方法的执行时间很长,计时器可能(在上个回调还没有完成的时候)再次触发。这可能造成多个线程池线程同时执行你的回调方法。并且线程切换也会造成诸多损耗时间。

4.阻塞现象原因分析

上面的方法中使用局部变量来创建指向一个线程定时器。因为局部变量会被GC回收,导致定时器失效。
具体改进如下:

static int i=0;
static Timer _timer = null;
protected void PollClient()
{
_timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite) ;
}
private void TimerCallback(object state)
{
try
{
i++; if (deviceContextList.Count > 0)
{
var deviceContext = GetDeviceContext("123456789");
SendMessage(messageList[i % 7], deviceContext.tcpSession.writerContext);
logger.Info("客户端数量:" + deviceContextList.Count + "循环次数:" + i); }
else
{
logger.Info("客户端数量为0" + "循环次数:" + i);
Console.WriteLine("客户端数量为0" + "循环次数:" + i);
}
}
catch (Exception e)
{
logger.Error("定时测试下发报文异常:" + e);
}
finally
{
_timer.Change( 1000, Timeout.Infinite);
}
}

将定时器与计数变量设置为static是为了定时器不被GC回收。定时任务执行完成之后再设置下次调用时间间隔是为了该任务不过多占用线程池中的线程,节省线程切换时间等。

5.问题解决

可以看到任务已经被执行了86665次,优化后不再被GC回收。

最新文章

  1. .NET应用架构设计—面向查询的领域驱动设计实践(调整传统三层架构,外加维护型的业务开关)
  2. IOS整体项目层级构建
  3. HTML5之创新的视频拼图剖析式学习之二
  4. React Native 开发之 (04) 例子讲解
  5. java类加载机制
  6. Centos6.5 安装 RabbitMQ3.6.1
  7. Python学习(4)——for语句
  8. Mysql 导入导出数据结构及数据
  9. angularjs——路由篇
  10. SQL 表值函数/标量函数
  11. c++ Qt向PHP接口POST文件流
  12. python摸爬滚打之day28----黏包处理
  13. windows下Docker的安装
  14. EL表达式、JSTL标签库
  15. 吴恩达机器学习笔记36-正则化和偏差/方差(Regularization and Bias_Variance)
  16. REdis AOF文件结构分析
  17. 通过Oracle DUMP 文件获取表的创建语句
  18. 【IDEA】【8】上传已有项目到Git
  19. 在Emacs中启用Fcitx输入法
  20. lumen 单元测试的一些问题

热门文章

  1. Kubernetes环境cert-manager部署与应用
  2. 力扣每日一题2023.1.16---1813. 句子相似性 III
  3. Python关键字 asynico
  4. Windows MongoDB的安装及配置图文说明(非常详细)
  5. STM32F4库函数初始化系列:串口发送
  6. 记录一次vue部署docker步骤
  7. JZOJ 4253.QYQ在艾泽拉斯
  8. Word 设置段前分页
  9. Luffy项目:2、项目需求(2),项目库的创建,软件开发目录,Django配置文件介绍
  10. Shapefile导入MySQL