用C#的Thread做了一个简单计时器。为了让自己45分钟后就可以休息一次,45分钟过后会响音乐提示。
开始使用的TimeSpan相减的方式,在Thread的启动函数中也就是这样写的:
- public void CountTime()
- {
- while (true)
- {
- TimeSpan tsNew = new TimeSpan(DateTime.Now.Ticks);
- TimeSpan tsIn = tsNew - tsOld;
- if (tsIn.Minutes >= 1)
- {
- while (true)
- {
- TimeSpan tsNewer = new TimeSpan(DateTime.Now.Ticks);
- TimeSpan tsIner = tsNewer - tsNew;
- if (tsIner.Minutes >= 10)
- {
- //十分钟后线程重启
- tsOld = tsNew;
- break;
- }
- }
- }
- }
- }
后来发现这种方法效率太低了。当然,可以使用Thread.Sleep(20);这个函数降低CPU占用时间。其实中间加入Thread.Sleep(20);就可明显的降低CPU消耗。后来发现C#中的Thread中自带有函数join(),这个函数可以使线程等待一段时间。调用方法如下
th.Join(new TimeSpan(hours, minutes, seconds));在等待的这段时间里线程处于WaitSleepJoin状态。
当然也可以调用Thread.Sleep(millionseconds);这里提一下Sleep和Join的区别
当线程执行Sleep时系统就退出执行队列一段时间,当睡眠结束时,系统会产生一个时钟中断,从而使线程回到执行队列中恢复线程的执行。Sleep方法如果参数是0,代表这个线程应当挂起让其他等待的线程运行,这里cpu回重新分配控制权,也有可能是刚才的执行的程序。这样就会有cpu占用总是100%的情况发生。有时你界面死了,但是你还是可以移动鼠标。如果是Timeout.Infinite,就代表将使线程休眠,直到它被调用 Thread.Interrupt 的另一个线程中断或被 Thread.Abort 中止为止。
如果父线程先于子线程结束,那么子线程将在父线程结束的同时被迫结束。Thread.Join()方法使父线程等待,直到子线程结束。Join方法有返回值,当值为true,代表线程终止。false的话代表线程在等待了那段时间后还未终止。如果在线程Unstarted状态时,调用join()就会有ThreadStateException异常。如果线程已经终止,那么调用这个函数,会立即得到返回值。
例如下面的主程序
...
ThreadStart st = New ThreadStart(fun);
Thread th = new Thread(ThreadStart st);
th.Start();
Application.Exit();
...
//下面是fun函数
void fun()
{
while(true)
{
...
}
}
这段程序的唯一毛病就是有可能在主程序退出后,从线程还没有结束。(这个从线程真可怜...)
这里顺便再提一下线程的几个状态:
创建:当创建一个新进程时,也为该进程创建了一个线程。线程还可以创建新线程。
就绪:线程已获得除处理机外的所有资源。
运行:线程正在处理机上执行。
阻塞:线程因等待某事件而暂停运行。
终止:一个线程已完成。
但是C#的线程中多了几个状态:
Aborted,AbortRequested,Background,Running,Stopped,StopRequested,Suspended,SuspendRequested,Unstarted,WaitSleepJoin。
Abort()将导致ThreadState.AbortRequested调用Abort()的线程获得控制权之后导致ThreadState.Aborted,AbortRequested与Aborted的区别在于一个停止一个未停止。而Stopped则是线程终止。但是我反复试验多次发现当调用Abort()函数后,线程状态会变为Stopped。如何变为Aborted状态,还在研究中。其他几个状态差不多。都是调用相应的函数会产生相应的状态请求,还有过一段时间才能到底相应的状态。至于Unstarted是还未调用Start()函数,Running是调用Start()或者Resume()函数的结果。WaitSleepJoin是在等待I/O,或者是调用Join()方法。这里Join()和Sleep()方法的不同还在于调用Join()线程状态进入WaitSleepJoin,调用Sleep()线程状态还是Running。
挂起线程的方法是Suspend();调用这个方法后,线程处于SuspendRequest状态。Suspended()调用后如果线程仍然在执行join()方法,因为Suspended()要让线程到达安全点之后它才可以将该线程挂起,此时那线程状态就是SuspendRequested|WaitSleepJoin。但是这里的join里的时钟依然在计数。所以现在还不知道用什么方法来暂停这个join计数,当然也可以不使用join解决彻底暂停线程这个问题。现在不明白哪个Suspended()函数是干什么的,因为线程依旧在运行中。另外值得一提的是现在不提倡使用Suspend()和让线程调用Suspend()后再次恢复的Resume()方法。原因很简单,因为这两个方法是由另外的线程执行,另外的线程并不能准确的知道被Suspend()的线程处于何种状态,如某个类的构造函数执行时期,或者析构等。所以用这个函数来同步很危险。
另外,要注意的是Thread.Sleep(n)这个n不能精确的控制时间。如果你认为要线程要隔多长时间,这个控制就有问题。如果当前的线程是一个前台线程,那么Thread.Sleep(n)就要在大于n的时间才能退出。如果是后台进程,当主程序退出后,这线程就再也不能唤醒..悲惨..所以一般也建议不用Thread.Sleep()函数。另外Sleep函数也不能用于同步.peter说程序的Sleep函数代表了一个很烂的设计。
做了个计时器竟然引发了这些问题...疯...