值得一提的是,在实际情况下以上三点理论都皆可能不满足。在某些特别简单的情况下(例如匿名方法中完全不涉及局部变量和其他方法),编译器只会简单生成一个静态的方法来构造一个委托实例,因为这样可以获得更好的性能。
对于之前的案例,我们现在也将它进行一番改写,这样便可“避免”使用匿名对象,也可以清楚地展现出问题原因:
- private class AutoGeneratedClass {
- public List<Item> m_batchItems; public void WaitCallback(object o)
- { DataContext db = new DataContext();
- db.Items.InsertAllOnSubmit(this.m_batchItems); db.SubmitChanges();
- } }
- static void Process() {
- var helper = new AutoGeneratedClass(); helper.m_batchItems = new List<Item>();
- foreach (var item in ...) {
- helper.m_batchItems.Add(item); if (helper.m_batchItems.Count > 1000)
- { ThreadPool.QueueUserWorkItem(helper.WaitCallback);
- helper.m_batchItems = new List<Item>(); }
- } }
编译器会自动生成一个AutoGeneratedClass类,并且在Process方法中使用这个类的实例来代替原来的batchItems局部变量。同样,交给ThreadPool的委托对象也从匿名方法变成了AutoGeneratedClass实例的公有方法。因此线程池每次调用的便是该实例的WaitCallback方法。
现在问题应该一目了然了吧?每次把委托交给线程池之后,线程池并不会立即执行,而会保留到合适的时间再进行。而WaitCallback方法在执行时,它会读取m_batchItems这个Field字段“当前”所引用的对象。而与此同时,Process方法已经“抛弃”了原本我们要提交的数据,因此会引起提交到数据库中数据的丢失。同时,在准备每批次数据的过程中,很有可能会发起两次数据提交,两个线程提交同样一批Item时,就抛出了所谓“莫名其妙”的异常。










