2013-06-28 59 views
1

背景:C#从静态类访问函数的调用线程之外

我对此需要超过几秒钟来完成更多的多个操作的类。在此期间,我想更新UI。所以BackgroundWorker通常是最好的选择。但由于某种原因,BackGroundWorker并不总是以我想要的方式工作(例如:当我尝试对事件使用WebBrowser并调用ReportProgress事件时,BackgroundWorker看起来崩溃)。

因此,我通过将Ui从主线程分离来避免所有这些。

此伪更好地解释它:

public Ui ui; 

main 
{ 
    Thread threadUi = new Thread(initiateUi);  
    //Initiate and start Thread 

    //Everything I will do from here on will not have any consequences 
    //on my ui. 
    // 
    //Object Ui can still be publicly accessed, making it possible to 
    //update the user interface. 
} 

现在,当我有Bar类的一个实例,我将使它的UI这样的访问:

public Bar bar1; 
public Bar bar2; 

main 
{ 
    // 
    //other stuff here 
    // 

    Thread threadBar1 = //New Thread where I call the Bar initializer function 
         //and pass bar1 as parameter. 
    Thread threadBar2 = //idem dito, except with bar2 as parameter 

    // 
    //other stuff here 
    // 
} 

有了这个设计,我可以打电话bar1和bar2从我的用户界面使用以下功能:

Program.bar1.someFunction(); 

问题:

现在让我们说我有一个名为FooHandler的类。该类有一个函数,用于在某个FooDepository中搜索Foo的所有实例,以及其他函数来操纵Foo对象。这是一个静态类,因为在我的情况下,它不需要有多个实例。

但是,如果我要从FooHandler调用一个函数,该函数将在我的UI线程中运行,因为这是调用线程(我不太确定,但我找不到有关此主题的任何文档)。所以我很有可能面临我开始的问题。

问题:

是否有可能访问静态类的功能,而无需使用来自调用线程的处理能力?

回答

2

可以通过使用另一个线程调用此函数。如果您使用.NET 4,请查看Task对象,这将轻松解决问题。例如,如果函数返回字符串,则需要调用函数的Task<string>。然后根据你的逻辑,你会阻止,直到它完成或做类似的事情。如果您使用的是.NET 4.5,那么使用异步/等待更容易。

+0

我正在使用.Net 4.0。所以我会研究这个Task对象。 编辑:哇!使用任务就像下面这样简单: 任务t =新任务(new Action(()=> {FooHandler.someFunction();}));? – Jordy

+1

是的,这很容易:)很高兴帮助。请使用通用版本的任务,然后你可以做t.Result(这将阻止线程,所以要小心)返回你的类型。此外,您不需要执行新的操作,下面是一个示例:任务 task = new任务(()=> FooHandler.someFunctionThatReturnsString()); –

3

首先:方法范围(定义它的地方)与程序流程没有任何关系。在何处定义方法(FooHandler,BarProvider或ThreadX)不会影响其调用的位置。实际上,方法总是在调用者的线程中调用。

因为您没有提及任何模型,视图或视图模型,并在标题中显示“c#”,我假设您在谈论WinForms。

在WinForms UI控件需要从用于创建它们的线程(通常是主线程)调用(更新)。所有的UI控件都实现了ISynchronizeInvoke接口,这意味着要做到这一点。因此,而不是常规的:

progress.Position = 7; 

你需要调用Invoke

progress.Invoke(new Action(() => progress.Position = 7), null) 

因为有很多的样板代码,我写了一个小的扩展功能,用于自己:

public static class ControlExtensions 
{ 
    public static void Synchronize(this Control control, Action action) 
    { 
     if (control == null || !control.InvokeRequired) 
     { 
      action(); 
     } 
     else 
     { 
      control.Invoke(action, null); 
     } 
    } 
} 

所以现在你可以:

progress.Synchronize(() => progress.Position = 7); 

(打字少一点,易于阅读)

从技术上讲,ISynchronizeTarget上的调用并不真正调用给定的动作。它只是在消息队列中放入一条消息(好的旧WM_xxxx)(但是在调用者的线程中这样做),委托作为参数。然后,如果目标(控制)线程正在处理消息(在它自己的线程中),它将获取此WM_xxxx消息,并调用委托(在调用者线程中 - 但这次是UI线程)并返回。

如果你需要新的线程调用FooHandler,你不想等待使用任务(它可能是最简单的方法):它不会等待(不会阻止用户界面

Task.Factory.StartNew(() => FooHandler.SearchOrWhatever(...)); 

线)。

尽管所有这一切都说了,不要以为它已经完成。 多线程很难。而所有那些支持的构造可以帮助您节省打字的时间,但困难的部分仍然存在:死锁,竞态条件,挨饿等。

+0

对于'方法范围(它被定义的地方)+1与程序流程无关“作为第一行。 –