背景:
我有一个包含多个操作的类,需要几秒钟以上才能完成。与此同时,我想更新用户界面。所以通常 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 线程中运行,因为那是调用线程(我不太确定,但我找不到关于这个主题的任何文档)。所以我很有可能要面对我开始时遇到的问题。
问题:
是否可以在不使用调用线程的处理能力的情况下访问静态类的函数?
最佳答案
首先:方法范围(定义它的地方)与程序流程无关。定义方法的位置(FooHandler、BarProvider 或 ThreadX)不会影响它的调用位置。实际上方法总是在调用者的线程中被调用。
因为您没有提到任何模型、 View 或 View 模型,而且标题中写着“c#”,所以我假设您在谈论 WinForms。
在 WinForms 中,UI 控件需要从用于创建它们的线程(通常是主线程)调用(更新)。所有 UI 控件都实现了 ISynchronizeInvoke 接口(interface),这就是为了做到这一点。所以,而不是常规的:
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)放入消息队列(但在调用者的线程中执行),并以委托(delegate)作为参数。然后,如果目标(控件的)线程正在处理消息(在它自己的线程中),它会收到此 WM_xxxx 消息,调用委托(delegate)(在调用者线程中 - 但这次是 UI 线程)并返回。
如果您需要新线程来调用 FooHandler,并且您不想等待,请使用任务(这可能是最简单的方法):
Task.Factory.StartNew(() => FooHandler.SearchOrWhatever(...));
它不会等待(不会阻塞 UI 线程)。
尽管说了这么多,但不要以为它已经完成了。 多线程很难。所有那些支持省去打字的构造,但困难的部分仍然存在:死锁、竞争条件、饥饿等。
关于C# 从调用线程外部的静态类访问函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17358979/