C#编译时如何确保方法仅在UI线程上调用,而其他方法仅在后台(非UI)线程上调用

5

现在我的做法是:

Util.AssertBackgroundThread();

或者

Util.AssertUIThread();

在方法开头。虽然这不算太糟糕,但这是运行时错误检查。我们使用C#等静态语言的原因是将更多的错误检查移至编译器。

现在我认为这通常不容易实现,但如果我只限制自己从自己的实用方法启动线程(或使用ThreadPool.QueueUserWorkItem),那么我认为如果标记了这些方法,则应该可以进行静态分析以验证仅应在UI线程上运行的方法确实只在UI线程上运行?

所以这里有两个问题。

  1. 我是否正确,这可以在编译时进行检查?
  2. 在Visual Studio 2008中(安装了最新的ReSharper)是否有任何实际方法来实现这一点?
2个回答

5

我一直喜欢这个模式:

public void GuiMethod(object param)
{
   if(this.InvokeRequired)
   {
      this.Invoke(delgateToGuiMethod, params,...)
   }
   else
   {
      //perform gui thread method
   }
}

您需要承担调用和检查的惩罚,但是您可以通过这种模式保证方法要么在GUI线程上运行,要么将被调用到GUI线程上。


2
我能想到的唯一帮助你的方法是使你的断言在其主体中使用#if DEBUG,这样在发布时这些方法就是空的。
例如:
public static void AssertUIThread()
{
#if DEBUG
   //the code goes here 
#endif
}

这样,您可以在开发过程中检查是否适当地调用了方法,并且JIT将在生产代码中完全删除该调用。
目前我不知道如何在编译时执行此操作,但我会将此问题标记为收藏,希望有人能回答。
越想越觉得,您可能可以使用自定义的FxCop规则进行后编译来实现您想要的功能。问题是...我不熟悉FxCop提供的Introspection API,并且它没有很好的文档说明。或者说,根本没有文档。我能为您做的最好的事情就是提供教程两个链接,这可能有助于您。我正在阅读它们中间的内容;如果我发现有趣的东西,我会发布它。
啊哈!你可以分析 方法的调用者和被调用者。使用指定的教程,创建一个专门针对应该始终从 UI 线程中调用的方法的属性,以及另一个专门针对应该仅从单独线程中调用的方法的属性。您的自定义规则检查这些属性之一,并且仅在方法具有该属性时才运行。然后,它会分析该方法的调用者(以及它们的调用者等等,递归地),直到可以确定调用者是在 UI 线程上还是来自新线程。

现在我们来到了棘手的部分。我还没有能够解决这部分问题,我把它留给你看看你能想出什么解决方案,因为现在时间很晚,我不能花太多时间来解决问题,但我非常感兴趣。我一直遇到的问题是,所有线程都是使用委托启动的,而且我感觉在调用者链条上进一步进行可能会有麻烦。我不知道是否有可能到达委托;如果可能的话,可以将委托类型与已知的线程委托进行比较,以确定调用是否在新线程上进行。

即使这是可能的,仍然存在通过委托进行操作的问题。如果不能这样做,你只能确定在第一个委托之前是否有新线程产生。
因此,需要解决一些问题。但愿这对你是一个第一步。

我已经做到了,实际上我让所有的Assert*方法都调用一个实际执行工作的Assert方法,并且它在其主体周围有#if DEBUG。我相信JIT足够聪明,可以删除空调用链。 - Eloff
3
@Eloff:给 Assert 方法打上 [Conditional("DEBUG")] 标记。这就是为什么它存在的原因 ;) - Sam Harwell
@280z28:做事情的更好方式。我已经忘记了那个。 - Randolpho
我原本希望能回来尝试一下这个,但生活在这段时间变得更加繁忙,而不是更轻松。我仍然很好奇它是否能够实现,但目前我必须采用一个更简单的解决方案。 - Eloff

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