思想决定人生,态度改变一切

成功者找方法,失败者找借口! 做事先做人;安分做人,本分做事!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
问题描述:首先有一个treeview控件,它的treenode节点分为两类,feed和category,一个feed节点对应着一个xml文件,一些相同种类的feed放在一个category下(相当于feed型节点的父节点),即一个category节点对应几个xml文件(当然了,category是可以嵌套的)。当用户点击一个treenode时,将由一个web控件来显示treenode对应的xml文件的内容。(如果是feed类型的节点就显示对应的一个xml文件,如果是category类型的节点就显示此category下的所有的feed对应的xml文件)。由于所用的web控件的navigate方法的参数只支持html文件名或是指定了xslt格式的xml文件名(不支持流格式的参数)所以需要一个临时的xml文件用来在用户点击的了treenode时,将对应的一个或多个xml文件的内容保存到这个临时文件中再在web控件中进行显示。此时问题也就产生了,假如用户在两次点击treenode的时间间隔很短,那么就会存在第一次向临时文件中的写入和保存工作没有完成就开始了第二次的写入,那么就会出现错误。虽然这种bug的出现的击率很小,但是不能排除不出现的可能(比如某一个category下的feed很多,并且每个feed对应的xml文件都很大,并且用户确实以相当快速的速度点击了两个不同的节点)。
    
问题现象:一旦出现了这样的错误,系统就汇报错"itemsdescription.xml文件正在由另一个进程使用"(itemsdescription.xml就是那个临时文件)。
    
问题的解决方案:
1、将临时文件的写入和保存工作放到一个方法中,并把这个方法放到系统线程池的方法队列中等待执行。
将耗时的任务从UI线程中剔除,放到工作线程或是线程池线程中      
复制  保存
ThreadPool.QueueUserWorkItem(new WaitCallback(AsynSaveTempXml),treenode);

2、在异步执行的方法中对临时文件进行操作,通过声明一个全局的XmlDocument 对象xmlDoc以实现对对象的线程同步。
注意对对象的线程同步
复制  保存
private void AsynSaveTempXml(TreeNode treenode)
{
lock (xmlDoc)
{
//... ...xmlDoc的写入
        //... ...xmlDoc.Save(filepath);
    }
myTree.BeginInvoke(new PopulateWebBrowserHandler(this.PopulateWebBrowser));
}

3、从UI线程中异步调用UI的刷新方法(即,填充web控件),不要在工作线程或线程池线程的方法中对UI进行处理,一定要通过BeginInvoke方法回到UI线程进行操作。

回过头来看,问题的解决很简单(对于高手来说)。但还是有一些原则性的问题需要留意的:

1、对于比较耗时(或是在某些情况下可能会比较耗时)的任务一定要异步来执行,否则就可能会出现问题,比如像我遇到的问题这样或者UI线程的阻塞etc。不要吝啬于线程切换所产生的开销,这一点开销远比单线程所带来的问题要强上许多。

2、.net下的用于线程同步的机制应该不止lock这一种,但是作为比较轻量级的用于对象的同步,monitor就已经足够了(lock实际上就是monitor的两个方法的封装---enter和exit),当然了,如果是进程同步就另当别论了。

3、如果没有特殊原因也没有必要自定义线程,完全可以用线程池来实现时异步机制。当然了,如果你需要对线程的优先级进行设置,或者需要获取某些线程的属性例如:name等等还是应该是用自定义线程的。

4、不要在工作线程中对UI进行修改,一定要通过Control.BeginInvoke()方法来调用修改UI的方法。
posted on 2008-01-22 13:57  投石问路  阅读(323)  评论(0编辑  收藏  举报