C#定时器的使用

之前做一个简单的btn点击后5s内不能再次点击的功能,用system.Timers.timer一直报线程占用,然后经查找学习了timer的使用,System.Timers.Timer是多线程计时器,对同一btn的操作会报线程占用(btn的click事件和计时器事件)。使用单线程计时器System.Windows.Threading.DispatcherTimer就行了。代码如下:

using System.Windows.Threading;
namespace test3.12
{
    public partial class MainWindow:Window
    {
        private System.Windows.Threading.DispatcherTimer timer =new DispatcherTimer();
        public MainWindow()
        {
            InitializeComponent();
            timer.Interval = new TimerSpan(0,0,5);//设置时间发出时间间隔(时,分,秒)
            timer.Tick+=new EventHandler(timer_Tick);//绑定事件 
        }

        void timer_Tick(object sender,EventArgs e)
        {//计时开始5s后触发该方法
            btn.IsEnable=true;//启用btn
            timer.Stop();//计时结束
        }

        private void btn_Click(object sender,RouterEventArgs e)
        {
            btn.IsEnable=false;
            timer.Start();
        }
    }
}

具体文章地址https://blog.csdn.net/m1654399928/article/details/21713311

此处是转载


在Framework中存在着4种定时器:其中分为两类,

多线程计时器

1:System.Threading.Timer

2:System.Timers.Timer

特殊目的的单线程计时器:

1:System.Windows.Forms.Timer(Windows Forms Timer)

2:System.Windows.Threading.DispatcherTimer(WPF timer);

多线程计时器比较强大,精确,而且可扩展性强;

单线程计时器的优点:
单线程计时器比较安全,对于更新 Windows Forms controls或者WPF这种简单任务来说更方便

你可以忘记线程安全。
一个Tick事件在前一个Tick事件被处理完毕前不会被触发。
你可以直接在Tick事件处理代码中更新控件,不需要调用Control.Invoke或Dispatcher.Invoke.

单线程计时器的缺点:除非Tick事件的处理代码执行的非常快,否则UI界面会变得响应很慢。

所以 WPF和Windows Forms的计时器都非常适合小任务,尤其是界面更新的任务。例如时钟和计数显示。否则,你需要一个多线程计时器

一 System.Threading.Timer是最简单的多线程计时器。在下面的例子中,定时器在5秒后开始定时1秒的调用Tick方法。

public static void Main ()
{
//5秒后开始运行,接着每隔1秒的调用Tick方法
Timer tmr = new Timer(Tick, “tick…”, 5000, 1000);
Console.ReadLine();
tmr.Dispose();
}
static void Tick(object data)
{
Console.WriteLine(data);
}

二 System.Threading.Timer进行了包装。增加了下面几个特性。

实现了Component,所以可以在设计器显示。
代替Change方法的一个Interval属性
代替callback委托的一个Elapsed事件
启动和停止timer的Enabled属性,默认是false。
为了避免Enabled造成混乱,提供了Start和Stop方法。
是否在每次指定的间隔结束时引发Elapsed时间,还是仅间隔第一次结束后运行的AutoReset属性。
在WPF或Windows Forms中安全的调用方法的SynchronizingObject对象。
public static void MainThread()
{
Timer tmr = new Timer();
tmr.Interval = 500;
tmr.Elapsed += new ElapsedEventHandler(tmr_Elapsed);
tmr.Start();
Console.ReadLine();
tmr.Stop();
Console.ReadLine();
tmr.Start();
Console.ReadLine();
tmr.Dispose();
}

static void tmr_Elapsed(object sender, ElapsedEventArgs e)
{
Console.WriteLine(“Tick…”);
三 如果运行下面的代码,会发现UI界面响应速度很慢,

//基于Windows消息循环的单线程计时器
private System.Windows.Forms.Timer timer = new Timer() { };

public Form1()
{
InitializeComponent();

timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;
}

void timer_Tick(object sender, EventArgs e)
{
//模拟的做一些耗时的操作
System.Threading.Thread.Sleep(2000);
}
原理上面已经介绍了:单线程计时器基于Windows消息循环,应用程序会同步的处理计时器的消息。

解决这个问题的方法是使用多线程计时器:只要修改代码使用多线程计时器即可:

//使用多线程计时器
private System.Timers.Timer timer = new System.Timers.Timer();

public Form1()
{
InitializeComponent();

timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Enabled = true;
}

void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
//模拟的做一些耗时的操作
System.Threading.Thread.Sleep(2000);
}

在.NET Framework里面提供了三种Timer

① System.Windows.Forms.Timer

② System.Timers.Timer

③ System.Threading.Timer

现分述如下:

一、System.Windows.Forms.Timer

1、基于Windows消息循环,用事件方式触发,在界面线程执行;是使用得比较多的Timer,Timer Start之后定时(按设定的Interval)调用挂接在Tick事件上的EvnetHandler。在这种Timer的EventHandler中可 以直接获取和修改UI元素而不会出现问题–因为这种Timer实际上就是在UI线程自身上进行调用的。

2、它是一个基于Form的计时器
3、创建之后,你可以使用Interval设置Tick之间的跨度,用委托(delegate)hook Tick事件
4、调用Start和Stop方法,开始和停止
5、完全基于UI线程,因此部分UI相关的操作会在这个计时器内进行
6、长时间的UI操作可能导致部分Tick丢失

例如

复制代码 代码如下:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

int num = 0;

private void Form_Timer_Tick(object sender, EventArgs e)
{
label1.Text = (++num).ToString();
Thread.Sleep(3000);
}

private void button1_Click(object sender, EventArgs e)
{
Form_Timer.Start();
}

private void button2_Click(object sender, EventArgs e)
{
Form_Timer.Stop();
}
}

实例解析

1、上面这个是一个很简单的功能,在Form窗体上拖了一个System.Windows.Forms.Timer控件名字为Form_Timer,在属性窗中把Enable属性设置为Ture,Interval是定时器的间隔时间。双击这个控件就可以看到 Form_Timer_Tick方法。在这个方法中,我们让她不停的加一个数字并显示在窗体上,2个按钮提供了对计时器的控制功能。
2、执行的时候你去点击其他窗体在回来,你会发现我们的窗体失去响应了。因为我们这里使用Thread.Sleep(3000);让当前线程挂起,而UI失去相应,说明了这里执行时候采用的是单线程。也就是执行定时器的线程就是UI线程。
3、Timer 用于以用户定义的事件间隔触发事件。Windows 计时器是为单线程环境设计的,其中,UI 线程用于执行处理。它要求用户代码有一个可用的 UI 消息泵,而且总是在同一个线程中操作,或者将调用封送到另一个线程。
4、在Timer内部定义的了一个Tick事件,我们前面双击这个控件时实际是增加了一行代码

复制代码 代码如下:
this.Form_Timer.Tick += new System.EventHandler(this.Form_Timer_Tick);
然后Windows将这个定时器与调用线程关联(UI线程)。当定时器触发时,Windows把一个定时器消息插入到线程消息队列中。调用线程执行一个消息泵提取消息,然后发送到回调方法中(这里的Form_Timer_Tick方法)。而这些都是单线程进行了,所以在执行回调方法时UI会假死。所以使用这个控件不宜执行计算受限或IO受限的代码,因为这样容易导致界面假死,而应该使用多线程调用的Timer。另外要注意的是这个控件时间精度不高,精度限定为 55 毫秒。

二、System.Timers.Timer

  1. 用的不是Tick事件,而是Elapsed事件
  2. 和System.Windows.Forms.Timer一样,用Start和Stop方法
  3. AutoReset属性决定计时器是不是要发起一次事件然后停止,还是进入开始/等待的循环。System.Windows.Forms.Timer没有这个属性
  4. 设置对于UI控件的同步对象(synchronizing object),对控件的UI线程发起事件

例如

复制代码 代码如下:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

int num = 0;
DateTime time1 = new DateTime();
DateTime time2 = new DateTime();
//定义Timer
System.Timers.Timer Timers_Timer = new System.Timers.Timer();

private void button1_Click(object sender, EventArgs e)
{
//手动设置Timer,开始执行
Timers_Timer.Interval = 20;
Timers_Timer.Enabled = true;
Timers_Timer.Elapsed += new System.Timers.ElapsedEventHandler(Timers_Timer_Elapsed);
time1 = DateTime.Now;
}

void Timers_Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
label1.Text = Convert.ToString((++num)); //显示到lable
Thread.Sleep(3000);
}

private void button2_Click(object sender, EventArgs e)
{
//停止执行
Timers_Timer.Enabled = false;
time2 = DateTime.Now;
MessageBox.Show(Convert.ToString(time2-time1));
}
}

三、System.Threading.Timer

复制代码 代码如下:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int num = 0;
DateTime time1 = new DateTime();
DateTime time2 = new DateTime();
System.Threading.Timer Thread_Time;

private void button1_Click(object sender, EventArgs e)
{
//启动
Thread_Time = new System.Threading.Timer(Thread_Timer_Method,null,0,20);
time1 = DateTime.Now;

}

void Thread_Timer_Method(object o)
{
label1.Text = Convert.ToString((++num));
System.Threading.Thread.Sleep(3000);
}

private void button2_Click(object sender, EventArgs e)
{
//停止
Thread_Time.Dispose();
time2 = DateTime.Now;
MessageBox.Show(Convert.ToString(time2-time1));
}
}

实例解析

1、用Threading.Timer时的方法,和前面就不太相同了,所以的参数全部在构造函数中进行了设置,而且可以设置启动时间。而且没有提供start和stop方法来控制计时器。而且是以一种回调方法的方式实现,而不是通过事件来实现的。他们之间还是有区别的。

2、我们只有销毁掉对象来停止他。当你运行时,你会发现他和前面的Timers.Timer一样,是多线程的,主要表现在不会假死,调试运行报错。但跟让你奇怪的是,我们的代码竟然无法让她停止下来。调用了Dispose方法没有用。问题在那?然后有进行了测试,修改了间隔时间为100,200,500,1000,3000,4000。这几种情况。发现当间隔为500ms以上是基本马上就停止了。而间隔时间相对执行时间越短,继续执行的时间越长。这应该是在间隔时间小于执行时间时多个线程运行造成的。因为所有的线程不是同时停止的。间隔越短,线程越多,所以执行次数越多。

3、System.Threading.Timer 是一个简单的轻量计时器,它使用回调方法并由线程池线程提供服务。不建议将其用于 Windows 窗体,因为其回调不在用户界面线程上进行

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值