我们在写winfrom时经常会用到定时器,比如客户端通过socket定时向服务器发送检测包等。.netframework提供了一个定时器控件System.Windows.Forms.Timer,使用很方便。
其实通过C#多线程也可以实现定时器功能。我的做法大致为:新开一个线程用于计时,当时间间隔过后用事件timerover实现主窗体中的定时函数,再通过调用contro.invoke方法返回主窗体线程执行定时后的操作。
先定义两个类,timer类用于管理和配置定时器,interval_date用于计算两个时刻的间隔(例如2010年1月1日和2012年12月26日之间的毫秒),单位是毫秒,因此定时器的设置范围理论上可以无限大。原理不多说,下面放代码:
timer类代码:
public class timer
{
int count = 0;
public delegate void Deletimerover(int count);
public event Deletimerover timerover;
private int time;
public int Time
{
set { time = value; }
get { return time; }
}
private bool enable;
public bool Enable
{
set { enable = value; }
}
public timer()
{
}
public timer(int t)
{
time = t;
}
public void start_time()
{
Thread rd = new Thread(new ThreadStart(start));
rd.IsBackground = true;
count = 0;
rd.Start();
}
private void start()
{
DateTime dt_tem = DateTime.Now;
interval_date interval = new interval_date();
while (interval.interval_millisec(dt_tem,DateTime.Now) < time) ;
if (!enable)
{
return;
}
else
{
timerover(++count);
start();
}
}
}
interval_date类代码:
public class interval_date
{
public long interval_millisec(DateTime dt1,DateTime dt2)
{
long interval = 0;
interval += com_year(dt1,dt2);
interval += com_month(dt1,dt2);
interval += com_day(dt1,dt2);
interval += com_hour(dt1, dt2);
interval += com_minute(dt1, dt2);
interval += com_second(dt1, dt2);
interval += com_millisecond(dt1, dt2);
return interval;
}
private long com_year(DateTime dt1, DateTime dt2)
{
int year1 = dt1.Year;
int year2 = dt2.Year;
long interval = 0;
if (year1 != year2)
{
for (int i = year1 + 1; i < year2; i++)
{
for(int j = 1; j <= 12; j++)
{
interval += DateTime.DaysInMonth(i, j);
}
}
}
interval = interval * 24 * 3600 * 1000;
return interval;
}
private long com_month(DateTime dt1, DateTime dt2)
{
int year1 = dt1.Year;
int year2 = dt2.Year;
int month1 = dt1.Month;
int month2 = dt2.Month;
long interval = 0;
if (year1 != year2)
{
for (int i = month1 + 1; i <= 12; i++)
{
interval += DateTime.DaysInMonth(year1, i);
}
for (int i = 1; i < month2; i++)
{
interval += DateTime.DaysInMonth(year2, i);
}
}
else
{
for (int i = month1+1; i < month2; i++)
{
interval += DateTime.DaysInMonth(year1, i);
}
}
interval = interval * 24 * 3600 * 1000;
return interval;
}
private long com_day(DateTime dt1,DateTime dt2)
{
int year1 = dt1.Year;
int year2 = dt2.Year;
int month1 = dt1.Month;
int month2 = dt2.Month;
int day1 = dt1.Day;
int day2 = dt2.Day;
long interval = 0;
if (year1 == year2 && month1 == month2)
{
if(day2 == day1)
{
return 0;
}
interval = day2-day1-1;
}
else
{
interval += DateTime.DaysInMonth(year1, month1) - day1;
interval += day2-1;
}
interval = interval * 24 * 3600 * 1000;
return interval;
}
private long com_hour(DateTime dt1, DateTime dt2)
{
long interval = 0;
if (dt1.Year == dt2.Year && dt1.Month == dt2.Month && dt1.Day == dt2.Day)
{
if (dt1.Hour == dt2.Hour)
{
return 0;
}
interval += dt2.Hour - dt1.Hour - 1;
}
else
{
interval += 24 - dt1.Hour - 1;
interval += dt2.Hour;
}
interval = interval * 3600 * 1000;
return interval;
}
private long com_minute(DateTime dt1, DateTime dt2)
{
long interval = 0;
if (dt1.Year == dt2.Year && dt1.Month == dt2.Month && dt1.Day == dt2.Day && dt1.Hour == dt2.Hour)
{
if (dt1.Minute == dt2.Minute)
{
return 0;
}
interval += dt2.Minute - dt1.Minute - 1;
}
else
{
interval += 60 - dt1.Minute - 1;
interval += dt2.Minute;
}
return interval * 60 * 1000;
}
private long com_second(DateTime dt1, DateTime dt2)
{
long interval = 0;
if (dt1.Year == dt2.Year && dt1.Month == dt2.Month && dt1.Day == dt2.Day && dt1.Hour == dt2.Hour && dt1.Minute == dt2.Minute)
{
if (dt1.Second == dt2.Second)
{
return 0;
}
interval += dt2.Second - dt1.Second - 1;
}
else
{
interval += 60 - dt1.Second - 1;
interval += dt2.Second;
}
return interval * 1000;
}
private long com_millisecond(DateTime dt1, DateTime dt2)
{
long interval = 0;
if (dt1.Year == dt2.Year && dt1.Month == dt2.Month && dt1.Day == dt2.Day && dt1.Hour == dt2.Hour && dt1.Minute == dt2.Minute && dt1.Second == dt2.Second)
{
interval += dt2.Millisecond - dt1.Millisecond;
}
else
{
interval += 1000 - dt1.Millisecond;
interval += dt2.Millisecond;
}
return interval;
}
}
最后是主窗体代码:
public partial class Form1 : Form
{
public timer newtimer = new timer();
delegate void Time(int count);
public void TimeOver(int count)
{
labelX1.Text = count.ToString();
}
public Form1()
{
InitializeComponent();
newtimer.Time = 1000;
newtimer.timerover +=new timer.Deletimerover(newtimer_timerover);
}
void newtimer_timerover(int count)
{
Time dd = new Time(TimeOver);
this.Invoke(dd, new object[] { count});
}
private void buttonX1_Click(object sender, EventArgs e)
{
buttonX1.Enabled = false;
newtimer.Enable = true;
newtimer.Time = int.Parse(textBoxX1.Text);
newtimer.start_time();
}
private void buttonX2_Click(object sender, EventArgs e)
{
newtimer.Enable = false;
buttonX1.Enabled = true;
labelX1.Text = "0";
textBoxX1.Text = "1000";
}
}
一个文本框两个按钮,按钮一用来开启定时器,按钮二用来复位。
通过简单的验证,这种方法和.netframework提供的timer控件实现的定时误差在个位数(毫秒),也可以尝试用更精确的方法来测试一下。