从timer看一眼.net的编译机制
因为工作需要,制作了一个定时发送数据的windows service,用C#开发。其中碰上了一个很有意思的问题。
先看看程序大概的结构:
初始化
private void InitializeComponent()
{
this.timer1 = new Timer();
this.timer1.Elapsed += new ElapsedEventHandler(timer1_Tick);
}
service的onStart()
protected override void OnStart(string[] args)
{
timer1.Interval = 6000;
timer1.Start();
EventLog.WriteEntry(ServiceName +"Event","timer started 1");
}
定时器触发
private void timer1_Tick(Object source, ElapsedEventArgs e)
{
DateTime time = DateTime.Now;
TimeSpan dayspan=new TimeSpan(1,0,0,0);
time=time.Subtract(dayspan);
DateTime dateTime1=new DateTime(time.Year,time.Month,time.Day,0,0,0);
DateTime dateTime2=new DateTime(time.Year,time.Month,time.Day,23,59,59);
if (time.Hour==8&&time.Minute==30&&lastupdate.Day!=DateTime.Now.Day)
{
try
{
PSOInterfaceWraper PsoWraper = new PSOInterfaceWraper();
PSOInterfaceWraper.GeneralLedgerAsyncResult Result;
DateTime StartDate=dateTime1;
DateTime EndDate=dateTime2;
int amount=LedgerAsyncBLL(StartDate,EndDate);
Result = PsoWraper.GeneralLedgerAsync("00",StartDate,EndDate,this.TranslationCount,amount*100);
EventLog.WriteEntry(ServiceName +" Vnet","Result:"+ Result.Result+" "+Result.ErrorDescription+"Count:"+this.TranslationCount.ToString()+"---Amount:"+(amount*100).ToString()+" date:"+StartDate.ToString()+"~"+EndDate.ToString());
if (Result.Result==0)
lastupdate= DateTime.Now;
}
catch (Exception err)
{
EventLog.WriteEntry(ServiceName +" Vnet","Error:"+" "+err.Message);
}
}
}
实际运行中发现, EventLog.WriteEntry(ServiceName +"Event","timer started 1"); 运行正确,但是timer1_Tick中的代码并没有运行。
后来分析发现,工程的Refences中引用了VNetPSO.dll,但是没有把相应的文件copy,到相应目录。也就是说,运行时找不到此文件。
观察代码可知,引用VNetPSO.dll部分的代码只存在于timer1_Tick中。复制VNetPSO.dll到正确位置,问题排除。
由此发现.net所谓即时编译的一些特性。
在InitializeComponent()中,可看到this.timer1.Elapsed += new lapsedEventHandler(timer1_Tick);按照.net的即时编译的思路,开始认为这段代码会导致timer1_Tick被编译。但是如果是这样的顺序,在InitializeComponent中就会出现错误,导致程序不能正常运行。_那么OnStart得EventLog.WriteEntry(ServiceName +"Event","timer started 1");
也就不可能运行了。
所以,timer1_Tick实际是在Timer第一次触发的时候才被编译的。上面程序的例子中,就是在timer1启动了6秒之后。这时候发现VNetPSO.dll不存在,故此这部分代码被编译器忽略。也就造成了最开始出现的情况。
当然,前面只是一些推测,并没有方法能得到可靠的验证。但是,我认为推测应该基本合理。
至少,可以推测这么几个事实:
1 .net中的代码只有实际用到才会编译,除非强制进行预编译。
2 由于以上机制定时器变得更加不可靠,因为在定时器第一次触发的瞬间会导致编译,对于要求时间很高的程序可能会导致错过第一次执行的时间。



