父窗体处理子窗体事件时添加BindingList项触发InvalidOperationException异常
兄弟,这个问题我太熟了——你遇到的System.InvalidOperationException十有八九是跨线程操作UI绑定集合搞出来的!
问题根源
你的BindingList<Proxy>是绑定到UI控件(比如DataGridView)的对吧?子窗体的SiteScraped事件大概率是在后台工作线程触发的(比如用了异步爬虫、BackgroundWorker或者Task.Run)。WinForms有个铁规则:所有UI相关的操作(包括修改绑定到UI的集合)必须在创建控件的那个UI线程执行。第一次成功纯粹是巧合——比如第一次触发事件时,代码刚好跑在UI线程上,后续异步任务启动后,事件在后台线程触发,直接操作BindingList就触发线程安全异常了。
解决方案
解决起来很简单,只要在事件处理方法里先判断是否需要切换到UI线程,再执行集合操作就行,用Control.Invoke就能搞定:
private void ScraperForm_SiteScraped(object sender, SiteScrapedEventArgs e) { // 先检查当前线程是不是UI线程 if (this.InvokeRequired) { // 不是的话,把当前方法委托给UI线程执行 this.Invoke(new Action<object, SiteScrapedEventArgs>(ScraperForm_SiteScraped), sender, e); return; } // 到这里就是安全的UI线程环境了,放心添加项 foreach (var proxy in e.ScrapedProxies) { proxies.Add(proxy); } }
额外注意点
- 如果不想阻塞后台线程,也可以用
BeginInvoke(异步委托)替代Invoke,不过大部分场景下Invoke的同步执行已经足够满足需求。 - 你不需要担心
SiteScrapedEventArgs里的代理列表线程安全问题,因为我们已经切换到UI线程再读取它,不会出现并发冲突。
内容的提问来源于stack exchange,提问作者JohnWick
相关产品推荐
相关产品推荐

