跨线程异常 - 仅限环境

5
一个控件只能被创建它的线程访问——这点我知道。
以下是需要翻译的内容:
  1. 我有一个使用BindingList<>作为数据源的DataGridView。
  2. 我有一个非GUI工作线程,运行一些复杂的计算/比较等操作,然后将对象添加/编辑到BindingList<>中。
  3. 定时器会刷新GUI线程与BindingList<>之间的数据。

这段代码在非环境模式下运行完美无缺。但在环境模式下,当调用BindingList<>的Add()方法时,就会出现下面这个错误:

An Exception has occurred
EXCEPTION : Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
IN METHOD : get_Handle
AT LINE   : 0
CLASS     : System.Windows.Forms.Control

请注意,被违反控件的名称 为空...我认为如果更新 BindingList<> 存在问题,无论在环境中还是不在环境中都不会有影响。尽管如此,这就是我所看到的。 此外,即使抛出异常,.Add() 也能成功完成!! 显然,在我的生产环境中这不是什么大问题(还没有?),因为它只在 Studio 中发生; 而且,是的,我可以调用 GUI 线程来执行 Add,或者将 Adds 存储在一个地方供 GUI 线程以后检索... 我不是在寻找解决方法,而是更想知道这个问题的答案: 为什么错误只出现在 studio 中呢?

2
“环境”是什么意思? - SLaks
@SLaks 抱歉 - 在 Visual Studio 中运行程序。 - Chris Barlow
3个回答

4
如果是 MDA (Managed Debugging Assistant) 而不是运行时异常错误,则此错误仅在 VS 中发生。MDA 用于告诉您正在执行的操作通常是会在生产代码中出现问题的操作(即使它在您的机器上表现正常,也最终会出现问题)。
你应该调用 UI 线程来执行 Add 方法。
编辑:为了100%的彻底...没有 Reflector(因为我的过期了 - 你在听Red Gate吗?!),我猜测 Control 类会检查你是否在UI线程上,如果不是,则在后台线程上抛出异常,然后重新绘制UI。由于后台线程已经添加了该项,因此UI重绘会看到它并将其按预期绘制到UI上,但是您的后台线程仍然会看到异常,并且您可能正在捕获它或者正在终止后台线程(如果该BG线程是线程池线程,则在您的应用程序中这也许是可以容忍的)。

你是不是把Resharper和Reflector搞混了?还有,ILSpy是一个相当不错的替代品。 - CodesInChaos
谢谢您的认真!我可以肯定地告诉您,没有一个try-catch会默默地传递异常 - 我知道这是因为有一个try-catch打印了异常(这就是我在Studio中捕获它的方式),但在生产环境中并没有打印。此外,在Studio中,线程在异常后确实会停止,但在生产环境中,它仍然在运行!我必须相信异常在生产环境中没有出现。但我喜欢您关于UI线程仍然进行重绘的说法 - 这非常合理! - Chris Barlow

3
错误只出现在VS中,因为它是一种诊断消息,试图告诉您存在错误。在生产中,这些额外的检查默认是禁用的。因此,bug始终存在,但仅在调试期间通知您。
我对多线程WinForms编程不太熟悉,但我认为由于您在另一个线程上调用了绑定列表中的添加操作,因此更改通知也会在该其他线程中发生,并且由于您绑定了控件,因此它将在此类通知期间更改其状态。这意味着从错误的线程访问控件,这是一个错误。

1

我认为这种情况经常发生;然而,绑定错误通常会被静默忽略(例如,BindingSource.DataError)。Visual Studio捕获已处理的异常 - 以突出失败。

如果您有一个数据绑定控件(DataGridView)和绑定感知集合,并从另一个线程更新它,则会遇到麻烦。其“观察者”特性使其在UI线程上进行更新。我过去曾对此进行了破解,但我认为您应该完全禁用数据绑定并手动更新。而且不要忘记手动更新和后台编辑都应该同步访问,以免互相干扰。

根据您正在执行绑定的方式,您应该能够禁用它并在循环中手动刷新它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接