转载自: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. php图片上传

    //处理图片 private function imageDeal($param){ $arrType=array('image/jpg','image/bmp','image/png','image ...

  2. int string convert

    C++ int与string的转化 int本身也要用一串字符表示,前后没有双引号,告诉编译器把它当作一个数解释.缺省 情况下,是当成10进制(dec)来解释,如果想用8进制,16进制,怎么办?加上前缀 ...

  3. 如何利用PowerPoint2013制作阶梯流程图?

    制作阶梯流程图有哪些窍门呢?下面我们一起来看看吧: ①启动PowerPoint2013,单击菜单栏--插入--形状,选择方角矩形,在图中画出来. ②画好矩形,摆放到合适的位置,如下图所示. ③然后再次 ...

  4. ASP.NET中Request.ApplicationPath、Request.FilePath、Request.Path、.Request.MapPath

    1.Request.ApplicationPath->当前应用的目录 2.Request.FilePath->对应于iis的虚拟目录   如 URL http://mockte.com/1 ...

  5. Spring ApplicationContext的事件机制

    ApplicationContext的事件机制是观察者设计模式的实现,通过 ApplicationEvent 类和 ApplicationListener 接口,可以实现 ApplicationCon ...

  6. 杂谈之不同行业的Solr

    杂谈之不同行业的Solr 前几天去一家互联网创业公司面试搜索引擎开发工程师,结果被pass了,仍不住想来吐槽下.尽管当时面试没啥准备,也没表现好,但是也学到了不少东西.现在就随便吐槽一下吧. 本人是在 ...

  7. mapreduce (三) MapReduce实现倒排索引(二)

    hadoop api http://hadoop.apache.org/docs/r1.0.4/api/org/apache/hadoop/mapreduce/Reducer.html 改变一下需求: ...

  8. N个元素的集合划分成互斥的两个子集的数目

    前面这是寒假听马士兵老师讲的时候积累的语录.......... 1.php是水果刀,java是菜刀,刀法比较多,一年的和三年的区别很大. 2.nanicat连接mysql出现10061是服务没开启,却 ...

  9. COJN 0487 800301红与黑

    800301红与黑 难度级别:B: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 有一间长方形的房子,地上铺了红色.黑色两种颜色的正方形瓷砖. ...

  10. C#编译时出现“不安全代码只会在使用 /unsafe 编译的情况下出现”错误的解决

    原因是:在编译的代码里面有不安全类型unsafe方法或类!解决方法:将项目属性页中生成下的“允许不安全代码”复选框打上对勾即可,方法如下:项目属性对话框->生成->允许不安全代码块 选中即 ...