访问BackgroundWorker的DoWork事件处理程序中的类成员变量以及其他BackgroundWorker限制

时间:2010-03-18 01:36:48

标签: .net backgroundworker

问题1

在BackgroundWorker的DoWork事件处理程序中,访问(包括读取和写入)包含BackgroundWorker的类的成员变量是否安全?访问未在DoWork事件处理程序本身内声明的其他变量是否安全?

显然,DoWork不应该访问任何UI对象,例如WinForms应用程序,因为UI应该只从UI线程更新。但是访问其他(不是与UI相关的)成员变量呢?

我问的原因是我看到偶尔发表评论时谷歌说不允许访问成员变量。我现在能找到的唯一例子是a comment on this MSDN page,其中说:

  

请注意,如果BGW尝试访问或修改类级变量,则会导致异常。所有数据必须由代表和事件传递给它。

还有:

  

NEVER。决不。切勿尝试引用未在DoWork内部声明的变量。它似乎有时会起作用,但实际上你很幸运。

据我所知,MSDN本身并没有记录任何此类限制(尽管如果我错了,我会欣赏一个链接)。但是像这样的评论似乎不时出现。

(当然,如果DoWork确实访问/修改了主线程可以同时访问/修改的成员变量,则需要同步访问该字段,例如使用锁定对象。但是上面引号似乎要求全面禁止访问成员变量,而不仅仅是同步访问!)

问题2

为了使这成为一个更普遍的问题,除了上述之外,是否还有其他(没有记录?)限制,BackgroundWorker的用户应该注意这些限制?也许是“最佳实践”?

3 个答案:

答案 0 :(得分:1)

您在问题1中引用的评论错误。从CLR的角度来看,从BackgroundWorker访问表单类的成员是可以的(除了你说的控件之外,因为它们具有线程亲和性;但是访问非控制成员如整数很好)。是的,正如您所说,如果您这样做,那么您可以正确地同步访问:它始终处于多线程场景中。但糟糕的同步不会导致异常,因为第一个评论者建议:它只会导致旧的数据损坏(当然这要好得多!)。说你“幸运的是”,这是错误的。这不是运气问题;这是一个良好的同步问题。

为什么我用“从CLR的角度”来限定这句话?首先,因为多线程访问状态很困难。因此,虽然它对CLR没有问题,但对于人类编程CLR可能会有问题。其次,因为如果您的表单类包含BackgroundWorker所需的大量非UI内容,则可能表明应用程序结构不佳。这可能意味着应该将这些内容打包到一个对象中,而BackgroundWorker应该调用该对象上的方法,而不是摆弄Form对象的状态。

答案 1 :(得分:0)

如果变量是事件的成员,那没关系。

如果变量是一个成员,让我们说一个这个BackgroundWorker被实例化的表单,我认为你应该在读取或写入一个值之前锁定它,即使对于整数类型,例如整数,布尔值,字符串等。

请记住,.NET 3.5集合既不是线程安全的。

请密切关注this link,因为它表达了您可能感兴趣的主题的其他观点和相关链接。

答案 2 :(得分:0)

与大多数事情一样,这取决于。我想这位评论者写了这些警告,因为这些是常见的问题区域。遵循该建议将大大有助于让您摆脱困境,但这也是不必要的限制。如果您了解成员变量的使用经常导致BackgroundWorker出现问题的原因,您可以确定您的应用程序是否受到影响以及如何最好地解决问题。

DoWork内,对成员变量(也称为字段)的访问发生在工作线程中。第一个问题是在后台工作程序启动后,某个线程更新了成员变量以引用null或其他对象。如果可能,同步(C#中的synclock关键字或其他机制)是合适的。第二个问题是虽然引用可能是稳定的,但对象本身可能不是线程安全的。如果该类是.NET Framework的一部分,则该类的文档几乎总是解释它是否是线程安全的。如果它不是线程安全的,那么围绕对引用该对象的成员变量的访问以及对象本身的使用添加同步通常会解决此问题。

特别要注意引用Windows窗体GUI对象(例如控件)的成员变量。虽然这些引用通常是稳定的,但通常这些对象不是线程安全的。更糟糕的是,synclock无法解决问题,因为Windows窗体对象有一个特殊要求,即必须在最初与对象关联的GUI线程上进行对这些对象的调用。要调用这些对象,可以使用对象的InvokeBeginInvoke方法(在Control类中定义)强制代码在对象的GUI线程上运行。或者,GUI的更新可以放在ProgressChanged的{​​{1}}和RunWorkerCompleted事件中:与BackgroundWorker不同,这些事件是在GUI线程上引发的。

众所周知,多线程充满了陷阱。 DoWork有助于简化某些事情,但仍有许多陷阱。希望您看到对多线程编程的充分理解对于在您的应用程序中集成BackgroundWorker非常重要。