需要第二(和第三)意见我对此Winforms竞争条件的修复

cod*_*eim 12 .net c# multithreading .net-3.5 winforms

关于如何实现记录或给前景GUI元素提供状态的后台工作者,在博客等中有一百个例子.其中大多数包括处理产生工作线程和使用ShowDialog()创建前景对话之间存在的竞争条件的方法.但是,我发现一个简单的方法是强制在表单构造函数中创建句柄,这样线程就不能在创建句柄之前触发表单上的Invoke/BeginInvoke调用.

考虑一个使用后台工作线程记录到前台的Logger类的简单示例.

另外,假设我们不希望NLog或其他重型框架做一些如此简单和轻量级的事情.

我的记录器窗口由前台线程使用ShowDialog()打开,但仅在启动后台"worker"线程之后.工作线程调用logger.Log(),它本身使用logForm.BeginInvoke()在前台线程上正确更新日志控件.

  public override void Log(string s)
  {
     form.BeginInvoke(logDelegate, s);
  }
Run Code Online (Sandbox Code Playgroud)

其中logDelegate只是"form.Log()"的简单包装或其他可能更新进度条的代码.

问题在于存在的竞争条件; 当后台工作线程在调用前台ShowDialog()之前开始记录时,表单的Handle尚未创建,因此BeginInvoke()调用失败.

我熟悉各种方法,包括使用Form OnLoad事件和计时器来创建工作任务,直到OnLoad事件生成一个计时器消息,一旦表单显示就启动任务,或者如上所述,使用队列对于消息.但是,我认为只是强制对话框的句柄尽早创建(在构造函数中)可确保没有竞争条件,假设线程由创建对话框的同一线程生成.

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.handle(v=vs.71).aspx

MSDN说:"如果尚未创建句柄,引用此属性将强制创建句柄."

所以我的记录器包装了一个表单,它的构造函数做了:

   public SimpleProgressDialog() {
       var h = form.Handle; // dereference the handle
   }
Run Code Online (Sandbox Code Playgroud)

解决方案看起来太简单了.我特别感兴趣的是为什么看似过于简单的解决方案使用起来或不安全.

任何意见?我错过了别的什么吗?

编辑:我不是要求替代品.如果我没有询问如何使用NLog或Log4net等,我会写一个关于此应用程序的所有客户限制的页面,等等.

根据赞成票的数量,还有很多其他人也想知道答案.

MSN*_*MSN 3

如果您担心引用Control.Handle依赖于副作用来创建句柄,您可以简单地调用Control.CreateControl()来创建它。但是,引用该属性的好处是,如果该属性已经存在,则不会对其进行初始化。

至于假设创建了句柄这是否安全,您是正确的:只要在同一线程上生成后台任务之前创建句柄,您就可以避免竞争条件。