从timer看一眼.net的编译机制
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明。
http://blog.devep.net/virushuo/2003/12/26/timernet.html
因为工作需要,制作了一个定时发送数据的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 由于以上机制定时器变得更加不可靠,因为在定时器第一次触发的瞬间会导致编译,对于要求时间很高的程序可能会导致错过第一次执行的时间。




Comments
问题1,webform也是如此?恐怕不是,记得有一次,我更改了用户登录部分的代码,没有编译,然后就运行,结果到了那里提示出错。
问题2,定时触发。huo记得上次我说的我们学校工作日志的那个OA么,最后我把定时系统自动添加空日志做出来了,用了sql的作业,定时运行一个比较复杂的sql插入语句。搞定了。所以,我想,这个应该是驻留内存的程序监视系统时间,用此去激发一个事件。这样应该可以解决问题。
Posted by: NetFire(Fire.Rolland.Han) | December 26, 2003 6:59 PM
我要做的不是定时执行个sql语句这么简单。所以不能靠sql server。
现在功能已经实现了,但是这个错误引出的.net动态编译机制的问题让我有了点兴趣,打算挖一挖。
webform应该也是。虽然我还没有测试。
Posted by: virushuo | December 26, 2003 8:01 PM
我不是说定时执行个sql语句。我是说些一个程序驻留内存...
Posted by: NetFire(Fire.Rolland.Han) | December 27, 2003 11:36 AM