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

时间:2013-06-28 06:58:37

标签: c# multithreading static

背景

我有一个包含多个操作的类,需要花费几秒钟才能完成。与此同时,我想更新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 个答案:

答案 0 :(得分: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上的Invoke并不真正调用给定的操作。它只是在消息队列中放置一条消息(旧的WM_xxxx)(但在调用者的线程中执行此操作),并将委托作为参数。然后,如果target(控件)的线程正在处理消息(在它自己的线程中),它会得到这个WM_xxxx消息,调用委托(在调用者线程中 - 但这次是UI线程)并返回。

如果您需要新线程来调用FooHandler,并且您不想等待使用任务(这可能是最简单的方法):

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

它不会等待(不会阻止UI线程)。

尽管如此,不要以为它已经完成了。 多线程很难。所有那些构建哪些支持可以节省你打字,但困难的部分仍然存在:死锁,竞争条件,饥饿等等。

答案 1 :(得分:2)

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