C# 线程系列三 定时器线程

上一篇文章我们讲诉了自定义线程执行器和任务处理器

我们继续来讲解自定义线程的定时执行器,我们在很多场景下需要做到某些状态或者数据进行更新,如果事情很多很杂,很时候时候会创建很多不同的定时器那么势必会照成系统的消耗和性能低下的问题!今天我们来解决这一问题。

首先我们创建定时任务执行器基类

 1 /// <summary>
 2     ///
 3     /// </summary>
 4     public abstract class TimerTaskBase : BaseTask
 5     {
 6
 7
 8         /// <summary>
 9         /// 开始执行的时间
10         /// </summary>
11         public long StartTime { get; set; }
12
13         /// <summary>
14         /// 是否一开始执行一次
15         /// </summary>
16         public bool IsStartAction { get; set; }
17
18         /// <summary>
19         /// 结束时间
20         /// </summary>
21         public long EndTime { get; set; }
22
23         /// <summary>
24         /// 执行次数
25         /// </summary>
26         public int ActionCount { get; set; }
27
28         /// <summary>
29         /// 已经执行的次数
30         /// </summary>
31         public int AActionCount { get; set; }
32
33         /// <summary>
34         /// 间隔执行时间
35         /// </summary>
36         public int IntervalTime { get; set; }
37
38         /// <summary>
39         /// 制定执行次数的定时任务
40         /// </summary>
41         /// <param name="startTime">0表示立即执行,否则延迟执行,填写开始时间</param>
42         /// <param name="intervalTime">执行间隔时间,小于10毫秒,当10毫秒处理</param>
43         /// <param name="isStartAction">是否一开始执行一次</param>
44         /// <param name="actionCount">需要执行的次数</param>
45         public TimerTaskBase(long startTime, int intervalTime, bool isStartAction, int actionCount)
46         {
47             this.StartTime = startTime;
48             this.IntervalTime = intervalTime;
49             this.IsStartAction = isStartAction;
50             this.ActionCount = actionCount;
51             this.EndTime = 0;
52         }
53
54         /// <summary>
55         /// 制定结束时间的定时任务
56         /// </summary>
57         /// <param name="startTime">0表示立即执行,否则延迟执行,填写开始时间</param>
58         /// <param name="intervalTime">执行间隔时间,小于10毫秒,当10毫秒处理</param>
59         /// <param name="endTime">执行结束时间</param>
60         /// <param name="isStartAction">是否一开始执行一次</param>
61         public TimerTaskBase(long startTime, int intervalTime, long endTime, bool isStartAction)
62         {
63             this.StartTime = startTime;
64             this.IntervalTime = intervalTime;
65             this.IsStartAction = isStartAction;
66             this.ActionCount = 0;
67             this.EndTime = endTime;
68         }
69
70         /// <summary>
71         /// 制定开始时间,无限执行任务
72         /// </summary>
73         /// <param name="startTime">0表示立即执行,否则延迟执行,填写开始时间</param>
74         /// <param name="intervalTime">执行间隔时间,小于10毫秒,当 10 毫秒处理 建议 10 毫秒的倍数</param>
75         /// <param name="isStartAction">是否一开始执行一次</param>
76         public TimerTaskBase(long startTime, int intervalTime, bool isStartAction)
77         {
78             this.StartTime = startTime;
79             this.IntervalTime = intervalTime;
80             this.IsStartAction = isStartAction;
81             this.ActionCount = 0;
82             this.EndTime = 0;
83         }
84
85         public TimerTaskBase()
86         {
87             // TODO: Complete member initialization
88         }
89     }

上面的代码实现了,开始时间,间隔时间,结束时间和执行次数 的控制

那么我们来看看定时器线程的设计

 1 public class TimerThread
 2     {
 3         public TimerThread()
 4         {
 5             System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(Run));
 6             thread.IsBackground = true;
 7             thread.Start();
 8         }
 9
10         /// <summary>
11         /// 任务队列
12         /// </summary>
13         private List<TimerTaskBase> taskQueue = new List<TimerTaskBase>();
14
15         /// <summary>
16         /// 加入任务
17         /// </summary>
18         /// <param name="t"></param>
19         public void AddTask(TimerTaskBase t)
20         {
21             if (t.IsStartAction)
22             {
23                 //满足添加队列前先执行一次
24                 t.Run();
25             }
26             lock (taskQueue)
27             {
28                 taskQueue.Add(t);
29             }
30         }
31
32         public long GetDate()
33         {
34             return Convert.ToInt64(System.DateTime.Now.ToString("yyyyMMddHHmmssfff"));
35         }
36
37         //这里的线程同步器,不是用来通知的,
38         //只是用来暂停的,因为Thread.Sleep() 消耗开销比较大
39         ManualResetEvent mre = new ManualResetEvent(false);
40
41         /// <summary>
42         /// 重构函数执行器
43         /// </summary>
44         private void Run()
45         {
46             ///无限循环执行函数器
47             while (true)
48             {
49                 if (taskQueue.Count > 0)
50                 {
51                     IEnumerable<TimerTaskBase> collections = null;
52                     lock (taskQueue)
53                     {
54                         //拷贝一次队列 预防本次轮训检查的时候有新的任务添加
55                         //否则循环会出错 集合被修改无法迭代
56                         collections = new List<TimerTaskBase>(taskQueue);
57                     }
58                     //开始迭代
59                     foreach (TimerTaskBase tet in collections)
60                     {
61                         int actionCount = tet.AActionCount;
62                         long timers = GetDate();
63                         if ((tet.EndTime > 0 && timers > tet.EndTime) || (tet.ActionCount > 0 && actionCount >= tet.ActionCount))
64                         {
65                             //任务过期
66                             lock (taskQueue)
67                             {
68                                 taskQueue.Remove(tet);
69                             }
70                             continue;
71                         }
72                         //获取最后一次的执行时间
73                         long lastactiontime = tet.TempAttribute.getlongValue("lastactiontime");
74                         if (lastactiontime != 0 && Math.Abs(timers - lastactiontime) < tet.IntervalTime)
75                         {
76                             continue;
77                         }
78                         //记录出来次数
79                         tet.AActionCount++;
80                         //记录最后执行的时间
81                         tet.TempAttribute.setValue("lastactiontime", timers);
82
83                         //上面的代码执行情况是非常几乎不用考虑消耗问题
84
85                         //下面是任务的执行需要考虑消耗,
86
87                         //这里我们不考虑执行耗时问题,
88                         //我们我这里没有涉及到后台线程池
89                         //也没有具体的业务逻辑,所以都放到这里统一执行
90                         tet.Run();
91                     }
92                 }
93                 //暂停10毫秒后再次检查
94                 mre.WaitOne(10);
95             }
96         }
97     }

定时器为什么没有使用上一篇文章讲诉的自定义线程呢,是因为,上一篇文章的自定义线程是基于队列处理的,先进先出执行,而我们的定时器任务并非是基于队列的。所以需要单独定义。

那么我们先来实现一个每一秒执行的任务,

 1  /// <summary>
 2     /// 每秒执行的任务
 3     /// </summary>
 4     public class SecondsTimerTask : TimerTaskBase
 5     {
 6         /// <summary>
 7         /// 定义一秒执行一次的
 8         /// </summary>
 9         public SecondsTimerTask()
10             : base(0, 1000, false)
11         {
12
13         }
14
15         public override void Run()
16         {
17             Console.WriteLine(DateTime.Now.ToString("HH:mm:ss:ffff: ") + "我是 每秒 执行的任务");
18         }
19     }

我们来测试一下看看效果

1     class Program
2     {
3         static void Main(string[] args)
4         {
5             TimerThread timerThread = new TimerThread();
6             timerThread.AddTask(new SecondsTimerTask());
7             Console.ReadLine();
8         }
9     }

还算是我们的预想的效果吧,每秒执行一次

接下来我们创建每分钟执行一次

 1     public class MinuteTimerTask : TimerTaskBase
 2     {
 3         /// <summary>
 4         /// 定义一分钟执行一次的
 5         /// </summary>
 6         public MinuteTimerTask()
 7             : base(0, 1000 * 60, false)
 8         {
 9
10         }
11
12         public override void Run()
13         {
14             Console.WriteLine(DateTime.Now.ToString("HH:mm:ss:ffff: ") + "我是 每分钟 执行的任务");
15         }
16     }

按照执行次数的定时器

 1     /// <summary>
 2     /// 我是按照执行次数结束的
 3     /// </summary>
 4     public class CountTimerTask : TimerTaskBase
 5     {
 6         /// <summary>
 7         /// 定义一秒执行一次的
 8         /// </summary>
 9         public CountTimerTask()
10             : base(0, 1000, false, 10)
11         {
12
13         }
14
15
16         public override void Run()
17         {
18             Console.WriteLine(DateTime.Now.ToString("HH:mm:ss:ffff: ") + "我是 次数 执行的任务 我要执行:" + this.ActionCount + " 次 执行了: " + this.AActionCount + " 次");
19         }
20     }

测试一下

 1    class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             TimerThread timerThread = new TimerThread();
 6             timerThread.AddTask(new SecondsTimerTask());
 7
 8             timerThread.AddTask(new MinuteTimerTask());
 9             timerThread.AddTask(new CountTimerTask());
10
11             Console.ReadLine();
12         }
13     }

运行结果:

到此我们的定时器线程执行器已经完成,满足我们需要做到定时更新状态,合作检查数据等应用场景!

不知道对你有没有帮助呢?

请不了吝啬你的小手,给予一个评论吧,帮助到你了请给与鼓励,如果有不足之处还请多多指教!

时间: 2024-09-12 01:11:46

C# 线程系列三 定时器线程的相关文章

【线程系列三】线程的等待与唤醒机制

为了更高效的处理一些时间片短,任务量大的任务,我们可能会经常用到多线程.但是多线程的环境下,很容易出现线程并发问题,线程死锁就是很常见的一种并发问题.为了避免此类问题,我们会用到线程间的通信,而等待唤醒机制,就是线程间通信的一种形式. 等待唤醒机制用到的方法主要有: public final void wait() throws InterruptedException : 当前线程必须拥有此对象监视器.该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notif

线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁

当涉及到多线程共享数据,需要数据同步的时候,就可以考虑使用线程锁了.本篇体验线程锁的各种用法以及线程死锁.主要包括: ※ 使用lock处理数据同步※ 使用Monitor.Enter和Monitor.Exit处理数据同步※ 使用Mutex处理进程间数据同步※ 使用Semaphore处理数据同步※ 线程死锁 □ 使用lock处理数据同步 假设有一个类,主要用来计算该类2个字段的商,在计算商的方法之内让被除数自减,即被除数有可能为零.使用lock语句块保证每次只有一个线程进入该方法. class Th

线程基础知识系列(三)线程的同步

本文是系列的第三篇,前面2篇,主要是针对单个线程如何管理,启动等,没有过多涉及多个线程是如何协同工作的. 线程基础知识系列(二)线程的管理 :线程的状态,控制,休眠,Interrupt,yield等 线程基础知识系列(一)线程的创建和启动  :线程的创建和启动,join(),daemon线程,Callable任务. 本文的主要内容 何谓线程安全? 何谓共享可变变量? 认识synchronized关键字 认识Lock synchronized vs Lock 1.何谓线程安全 多线程是把双刃剑,带

pThread线程(三) 线程同步--条件变量

条件变量(Condition Variables) 参考资料:http://game-lab.org/posts/posix-thread-cn/#5.1 条件变量是什么? 条件变量为我们提供了另一种线程间同步的方法,然而,互斥量是通过控制线程访问数据来实现同步,条件变量允许线程同步是基于实际数据的值. 如果没有条件变量,程序员需要让线程不断地轮询,以检查是否满足条件.由于线程处在一个不间断的忙碌状态,所以这是相当耗资源的.条件变量就是这么一个不需要轮询就可以解决这个问题的方法. 条件变量总是跟

【java线程系列】java线程系列之java线程池详解

一线程池的概念及为何需要线程池: 我们知道当我们自己创建一个线程时如果该线程执行完任务后就进入死亡状态,这样如果我们需要在次使用一个线程时得重新创建一个线程,但是线程的创建是要付出一定的代价的,如果在我们的程序中需要频繁使用线程,且每个线程执行的时间很短,短到几乎小于线程创建及销毁的时间那么代价将会更大,如:服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的.显然如果频繁的创建销毁线程效率将非常低. 那么我们能否让一个线程可以复用,即当一个线程执行完后不销毁该线程,而

线程系列01,前台线程,后台线程,线程同步

在控制台应用程序集中,Main方法开始的是一个线程.如果要再创建线程,需要用到System.Threading这个命名空间. □ 创建第一个线程 using System; using System.Threading; namespace ConsoleApplication4 { class Program { static void Main(string[] args) { var thread = new Thread(DoSth); thread.Start(); } static

死磕 java线程系列之创建线程的8种方式

(手机横屏看源码更方便) 问题 (1)创建线程有哪几种方式? (2)它们分别有什么运用场景? 简介 创建线程,是多线程编程中最基本的操作,彤哥总结了一下,大概有8种创建线程的方式,你知道吗? 继承Thread类并重写run()方法 public class CreatingThread01 extends Thread { @Override public void run() { System.out.println(getName() + " is running"); } pub

【线程系列一】线程如何运行

1 public static void main(String[] args)throws Exception { 2 Thread th= new Thread(new MyRunnable("hello1")); 3 th.start(); 4 System.out.println("当前主线程 "+Thread.currentThread().getName()); 5 System.out.println("该线程 "+th.getNa

【线程系列二】线程的五种状态

线程有5种状态,分别是新建.受阻塞.运行.死亡.休眠.等待. 在api中的解释如下图1所示 图1 图2 解释一下上述图2的过程. 1.new一个线程对象,该对象的状态为"新建状态". 2.执行start(),如果cpu现在空闲,则切换到运行状态,否则切换到阻塞状态. 3.线程执行sleep(),切换到休眠状态,在休眠时间到期后,自动切换到(运行或阻塞) 4.执行wait(),切换到等待状态,如果不执行notifiy(),则一直处于等待状态. 5.在线程中的run()执行完毕后,线程切换