转载自:http://hi.baidu.com/wingingbob/item/9f1c9615f3b24d5f2b3e225c
基于多线程设计,计时器工作在ThreadPool线程上,存在事件的重入问题;
MSDN只是说基于服务器的计时器可能比Windows计时器精确得多,具体是多少,与线程计时器的精度有关(内部由线程计时器实现),但是我们可以相信它有十分准确的1毫秒;
通过Interval属性或者构造设定计时器触发时间,在Interval属性大于0时,并且Enabled属性为true,将引发Elapsed事件。当AutoReset属性被设置为false时,只引发一次Elapsed事件,不会周期性回调事件,其默认值为true。可以使用Start()方法和Stop()方法控制Enabled属性;
需要使用Close方法和Dispose方法销毁资源;注意:一旦服务器计时器对象不存在任何引用,垃圾回收器会回收该对象,因此,在引用失效之前需要使用GC.KeepAlive方法使它不被回收,建议将服务器计时器声明在类级别或更高,防止此问题的发生;
服务器计时器只能应用在.NET Framework中,不被.NET Compact Framework(掌上设备)和XNA Framework(游戏开发)支持。 System.Threading.Timer类(线程计时器)
前两个计时器(Windows计时器和服务器计时器)都继承了Component(组件)类,而且他们可以作为父类被再次继承。线程计时器则是更“轻量的”计时器,它是密封的,它的声明如下:
[ComVisible(true), HostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)]
public sealed class Timer : MarshalByRefObject, IDisposable
{
// Fields
private const uint MAX_SUPPORTED_TIMEOUT = 0xfffffffe;
private TimerBase timerBase; // Methods
[MethodImpl(MethodImplOptions.NoInlining)]
public Timer(TimerCallback callback);
[MethodImpl(MethodImplOptions.NoInlining)]
public Timer(TimerCallback callback, object state, int dueTime, int period);
[MethodImpl(MethodImplOptions.NoInlining)]
public Timer(TimerCallback callback, object state, long dueTime, long period);
[MethodImpl(MethodImplOptions.NoInlining)]
public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period);
[MethodImpl(MethodImplOptions.NoInlining), CLSCompliant(false)]
public Timer(TimerCallback callback, object state, uint dueTime, uint period);
public bool Change(int dueTime, int period);
public bool Change(long dueTime, long period);
public bool Change(TimeSpan dueTime, TimeSpan period);
[CLSCompliant(false)]
public bool Change(uint dueTime, uint period);
public void Dispose();
public bool Dispose(WaitHandle notifyObject);
private void TimerSetup(TimerCallback callback, object state, uint dueTime, uint period, ref StackCrawlMark stackMark);
}
从这份声明中可以看到,它公开了五个重载的构造函数、四个重载的Change方法和两个重载的Dispose方法,甚至连一个属性都没提供,只有构造、Change、Dispose这三样东西,我们发现,越是底层的,就越简洁,而简洁的,不意味着会简单。
构造函数
虽然有五个重载,但是第一个(上面声明代码中第10行)是不推荐的,因为它只工作在.NET框架上。这个构造函数只需要指定一个TimerCallback回调,而其他几个构造函数中都有另外三个参数。
解释一下这四个参数的意义:
第1个参数,TimerCallback回调。TimerCallback是个委托,处理计时器调用的方法。通过它使计时器到时间后执行我们写的方法,就像前两个计时器中所谓的事件,只不过这里用委托回调的方式实现的。但是要清楚,委托的方法并不是在创建Timer的线程上执行的,它会在系统提供的一个单独的线程池线程中执行。因此要在这个方法中访问创建Timer线程中的对象,需要在创建Timer的线程里再定义一个委托,同步线程后,通过这个委托去访问。
第2个参数,回调方法传递的对象。可以为null,主要看我们需要不需要有个对象。
第3个参数,启动时间。是第一次回调前延迟时间量。可以用整型数值类型,毫秒为单位,0是立即启动,Timeout.Infinite(即-)是无限大,相当于禁止;也可以用TimeSpan表示时间间隔。
第4个参数,周期间隔。在第一次回调之后,周期回调需要的时间间隔。Timeout.Infinite(即-)会禁止周期回调。同样可以用整型数值或者TimeSpan表示时间间隔。
五个构造函数就不列出来了,看前面的。解释下第一个构造函数,它只有第一个参数,其他三个参数会被依次设定为null, Timeout.Infinite, Timeout.Infinite。
Change方法
Change方法有四个重载,用它们可以随时修改计时器的启动时间和周期时间,它们的参数与构造函数的第3、4个参数意义相同。一个很实用的例子就是暂停计时器的周期回调:
Change(, Timeout.Infinite);
官方文档中:

如果 dueTime 是零 (0),會立即叫用回呼方法。 如果 dueTime 是 Infinite,則永不叫用回呼方法;會停用計時器,若要重新啟用,請呼叫 Change 並為 dueTime 指定正值。


如果 period 是零 (0) 或 Infinite,而且 dueTime 不是 Infinite,則只叫用回呼方法一次;會停用計時器的定期行為,若要重新啟用,請呼叫 Change 並為 period 指定正值。

Dispose方法 两个Dispose方法,都是用来释放该计时器使用的资源。在流程计时器使用完之后,一定记得执行该方法销毁资源。需要解释一下它的重载Dispose(WaitHandle),它只被.NET框架平台支持。它的作用是在释放完计时器的时候发出WaitHandle信号,而且在资源释放成功后会返回true。WaitHandle是个抽象类,而我们通常选择继承了它的AutoResetEvent类的对象来发送信号。有信号的好处是我们可以给销毁计时器预留一些时间,来等待计时器占用的资源被全部释放完之后再执行其他代码。 就目前来看,你只要会用AutoResetEvent类的Set方法和WaitOne方法就足够读懂下面的例子了。
虽然这个例子并没有演示带信号的Dispose的方法,但是它(MSDN)巧妙地利用AutoResetEvent对象作为计时器委托的参数与Main的线程交互,请认真阅读下面的每行代码和每句注释,你同时会掌握线程计时器和AutoResetEvent类的使用。 [例1]
using System;
using System.Threading; class TimerExample
{
static void Main()
{
// 将会作为参数传入计时器回调的方法中,我们通过它向Main函数发送信号。
AutoResetEvent autoEvent = new AutoResetEvent(false);
// StatusChecker是我们写的包含回调方法,进行状态检查的类。假设要检查5次。
StatusChecker statusChecker = new StatusChecker();
// 为计时器创建一个用于请求statusChecker.CheckStatus方法的委托
TimerCallback timerDelegate = new TimerCallback(statusChecker.CheckStatus); Console.WriteLine("{0} 创建计时器。\n", DateTime.Now.ToString("H:mm:ss.fff"));
// 创建计时器,用autoEvent作为委托方法的参数,计时器启动时间是1秒,周期间隔250毫秒,
// 也就是1秒后请求CheckStatus方法,之后每250毫秒请求一次。
Timer stateTimer = new Timer(timerDelegate, autoEvent, , ); // 在5秒内等待autoEvent信号
autoEvent.WaitOne(, false);
// 收到autoEvent信号后或者超过5秒钟的等待,改变计时周期间隔为500毫秒
// 由于计时器已经启动,启动时间设置为0就可以了。
stateTimer.Change(, );
Console.WriteLine("\n改变计时周期间隔。\n"); // 在5秒内等待autoEvent信号
autoEvent.WaitOne(, false);
// 在第二次收到autoEvent信号或者超过5秒钟,销毁计时器。
stateTimer.Dispose();
Console.WriteLine("\n销毁计时器。");
}
} class StatusChecker
{
int invokeCount, maxCount; public StatusChecker(int count)
{
invokeCount = ;
maxCount = count; //检查次数
} // 被计时器委托调用的方法
public void CheckStatus(Object stateInfo)
{
AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
Console.WriteLine("{0} 状态检查 {1,2}",
DateTime.Now.ToString("H:mm:ss.fff"), (++invokeCount).ToString());
if(invokeCount == maxCount)
{
// 计数清零,然后向Main函数发送信号。
invokeCount = ;
autoEvent.Set();
}
}
}
using System;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel; namespace MessageWindow
{
public partial class MessageWindow : Form
{
//声明一个线程计时器
System.Threading.Timer _timer;
//窗口自动关闭倒计时,如果没在构造时更改,默认为不关闭。
int _interval = System.Threading.Timeout.Infinite;
//关闭窗口的委托,其他线程通过这个委托来调用关闭当前窗口的代码。
delegate void CloseDelegate(); private MessageWindow()
{
InitializeComponent();
picIcon.Image = System.Drawing.SystemIcons.Information.ToBitmap();
//实例化计时器,回调方法到TimerTick,无参数,不启动,禁止周期回调。
_timer = new System.Threading.Timer(
new System.Threading.TimerCallback(TimerTick),
null,
System.Threading.Timeout.Infinite,
System.Threading.Timeout.Infinite);
//获得主屏幕的工作区矩形
Rectangle workArea = Screen.PrimaryScreen.WorkingArea;
//将窗口显示在屏幕右下方位置
StartPosition = FormStartPosition.Manual;
Location = new Point(workArea.Right - this.Width, workArea.Bottom - this.Height);
} public MessageWindow(string caption, string text)
: this()
{
lblCaption.Text = caption;
lblText.Text = text;
} /// <summary>
/// 消息窗口
/// </summary>
/// <param name="caption">消息标题</param>
/// <param name="text">消息内容</param>
/// <param name="interval">消息窗口自动消失时间</param>
public MessageWindow(string caption, string text, int interval)
: this(caption, text)
{
_interval = interval;
} protected override void OnLoad(EventArgs e)
{
//动画渐入
NativeMethods.AnimateWindow(this.Handle, , NativeConstants.AW_BLEND + NativeConstants.AW_ACTIVATE);
//用_interval启动计时器,不进行周期计时。
_timer.Change(_interval, System.Threading.Timeout.Infinite);
base.OnLoad(e);
} //计时器回调方法。这里的代码将在线程池线程上执行,调用UI线程窗口的Close方法需要请求线程同步,
//通过UI线程的CloseDelegate委托执行关闭窗口,用窗口的Invoke方法执行这个被委托的代码。
void TimerTick(object obj)
{
if (this.InvokeRequired)
{
CloseDelegate closeme = delegate
{
this.Close();
};
this.Invoke(closeme);
}
} //在窗口关闭时销毁线程计时器资源,动画渐出。
protected override void OnFormClosed(FormClosedEventArgs e)
{
if (_timer != null) _timer.Dispose();
NativeMethods.AnimateWindow(this.Handle, , NativeConstants.AW_BLEND + NativeConstants.AW_HIDE);
base.OnFormClosed(e);
}
}
}

DotNet中的计时器线程计时器的更多相关文章

  1. java核心知识点学习----并发和并行的区别,进程和线程的区别,如何创建线程和线程的四种状态,什么是线程计时器

    多线程并发就像是内功,框架都像是外功,内功不足,外功也难得精要. 1.进程和线程的区别 一个程序至少有一个进程,一个进程至少有一个线程. 用工厂来比喻就是,一个工厂可以生产不同种类的产品,操作系统就是 ...

  2. C#中种常用的计时器

    1.System.Timers.Timer和System.Windows.Forms.Timer,它的最低识别为1/18s. 2.timeGetTime,他的最低识别能达到5ms. 3.System. ...

  3. 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法

    [源码下载] 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法 作者:webabcd 介绍重新想象 Wi ...

  4. DotNet中人民币符号的输出

    DotNet中人民币符号“¥”的输出<html> <head>DotNet中人民币符号的输出</head> <body> <p>¥100元& ...

  5. Java中的守护线程和非守护线程(转载)

    <什么是守护线程,什么是非守护线程> Java有两种Thread:"守护线程Daemon"(守护线程)与"用户线程User"(非守护线程). 用户线 ...

  6. springmvc中request的线程安全问题

    SpringMvc学习心得(四)springmvc中request的线程安全问题 标签: springspring mvc框架线程安全 2016-03-19 11:25 611人阅读 评论(1) 收藏 ...

  7. Unity 中 使用c#线程

    使用条件   天下没有免费的午餐,在我使用unity的那一刻,我就感觉到不自在,因为开源所以不知道底层实现,如果只是简单的做点简单游戏,那就无所谓的了,但真正用到实际地方的时候,就会发现一个挨着一个坑 ...

  8. dotNet中初始化器的使用

    dotNet中初始化器的使用 2013年12月7日 13:27 有两类初始化器: 对象初始化器和集合初始化器 比如现在有一个User类: Public   class User { public in ...

  9. Java中的守护线程 & 非守护线程(简介)

    Java中的守护线程 & 非守护线程 守护线程 (Daemon Thread) 非守护线程,又称用户线程(User Thread) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守 ...

随机推荐

  1. MLlib 编程指导-spark-1.2.0

    本文来自 http://spark.apache.org/docs/latest/mllib-guide.html 官方文档翻译 个人翻译 MLlib包括的算法和工具主要有:分类,回归,聚类,协同过滤 ...

  2. css阴影

    文字阴影:text-shadow:[颜色 x轴 y轴 模糊半径],[颜色 x轴 y轴 模糊半径]... 区域阴影:box-shadow:[颜色 x轴 y轴 模糊半径],[颜色 x轴 y轴 模糊半径]. ...

  3. standing

    2bc*cosA=b^2+c^2-a^2 #include<cstdio> #include<cstring> #include<iostream> #includ ...

  4. CGlib使用案例

    实际对象: public class RealObject { public void doSomething() { System.out.println("RealObject.doSo ...

  5. 7.4.1 Dumping Data in SQL Format with mysqldump

    7.4 Using mysqldump for Backups 使用mysqldump 用于备份: 7.4.1 Dumping Data in SQL Format with mysqldump 7. ...

  6. HDOJ(HDU) 2103 Family planning(需要注意范围)

    Problem Description As far as we known,there are so many people in this world,expecially in china.Bu ...

  7. XBox360自制系统的更新(Update)

    升级和更新 升级(Upgrade):从Windows XP到Windows 10,这叫升级,不叫更新.XBox360升级失败的话,后果可能会比较严重,直接就无法开机了. 更新(Update):在Win ...

  8. http://aws.amazon.com/cn/ses/ 亚马逊 营销性非事务邮件发送平台

    http://aws.amazon.com/cn/ses/   亚马逊 营销性非事务邮件发送平台

  9. js打开新的链接下载文件

    var p =params.join("&"); var a = document.createElement('a'); a.href = 'report/exportp ...

  10. javascript 分离全局变量

    当要编写一段javascript模块代码是, 这段代码将要在用在各种javascript程序中(就是各种各样的网页中), 为了让这段代码不与原来的代码起冲突,解决方法就是将代码放在一个函数(funct ...