gpt4 book ai didi

c# - 如何从另一个线程调用 UI 方法

转载 作者:IT王子 更新时间:2023-10-29 04:33:10 25 4
gpt4 key购买 nike

玩计时器。上下文:带有两个标签的 winforms。

我想看看 System.Timers.Timer 是如何工作的,所以我没有使用 Forms 计时器。我知道表单和 myTimer 现在将在不同的线程中运行。有没有一种简单的方法可以用以下形式在 lblValue 上表示耗时?

我在 MSDN 上看过这里但是有没有更简单的方法!

这是 winforms 代码:

using System.Timers;

namespace Ariport_Parking
{
public partial class AirportParking : Form
{
//instance variables of the form
System.Timers.Timer myTimer;
int ElapsedCounter = 0;

int MaxTime = 5000;
int elapsedTime = 0;
static int tickLength = 100;

public AirportParking()
{
InitializeComponent();
keepingTime();
lblValue.Text = "hello";
}

//method for keeping time
public void keepingTime() {

myTimer = new System.Timers.Timer(tickLength);
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);

myTimer.AutoReset = true;
myTimer.Enabled = true;

myTimer.Start();
}


void myTimer_Elapsed(Object myObject,EventArgs myEventArgs){

myTimer.Stop();
ElapsedCounter += 1;
elapsedTime += tickLength;

if (elapsedTime < MaxTime)
{
this.lblElapsedTime.Text = elapsedTime.ToString();

if (ElapsedCounter % 2 == 0)
this.lblValue.Text = "hello world";
else
this.lblValue.Text = "hello";

myTimer.Start();

}
else
{ myTimer.Start(); }

}
}
}

最佳答案

我猜你的代码只是一个测试,所以我不会讨论你用你的计时器做了什么。这里的问题是如何在计时器回调中使用用户界面控件执行某些操作。

大多数 Control 的方法和属性只能从 UI 线程访问(实际上它们只能从您创建它们的线程访问,但这是另一回事)。这是因为每个线程都必须有自己的消息循环(GetMessage() 按线程过滤消息),然后要使用 Control 执行某些操作,您必须从您的线程到 线程。在 .NET 中,这很容易,因为每个 Control 为此目的继承了几个方法:Invoke/BeginInvoke/EndInvoke。要知道执行线程是否必须调用那些方法,您可以使用属性 InvokeRequired。只需更改您的代码即可使其正常工作:

if (elapsedTime < MaxTime)
{
this.BeginInvoke(new MethodInvoker(delegate
{
this.lblElapsedTime.Text = elapsedTime.ToString();

if (ElapsedCounter % 2 == 0)
this.lblValue.Text = "hello world";
else
this.lblValue.Text = "hello";
}));
}

请检查 MSDN 以获取可以从任何线程调用的方法列表,作为引用,您可以随时调用 InvalidateBeginInvokeEndInvokeInvoke 方法和读取 InvokeRequired 属性。一般来说,这是一种常见的使用模式(假设 this 是从 Control 派生的对象):

void DoStuff() {
// Has been called from a "wrong" thread?
if (InvokeRequired) {
// Dispatch to correct thread, use BeginInvoke if you don't need
// caller thread until operation completes
Invoke(new MethodInvoker(DoStuff));
} else {
// Do things
}
}

请注意,当前线程将阻塞,直到 UI 线程完成方法执行。如果线程的时间很重要,这可能是一个问题(不要忘记 UI 线程可能很忙或挂起一点)。如果您不需要方法的返回值,您可以简单地将 Invoke 替换为 BeginInvoke,对于 WinForms,您甚至不需要后续调用 EndInvoke :

void DoStuff() {
if (InvokeRequired) {
BeginInvoke(new MethodInvoker(DoStuff));
} else {
// Do things
}
}

如果您需要返回值,那么您必须处理通常的 IAsyncResult 接口(interface)。

它是如何工作的?

GUI Windows 应用程序基于具有消息循环的窗口过程。如果你用纯 C 编写应用程序,你会得到类似这样的东西:

MSG message;
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}

通过这几行代码,您的应用程序等待一条消息,然后将消息传递给窗口过程。窗口过程是一个很大的 switch/case 语句,你可以在其中检查你知道的消息 (WM_) 并以某种方式处理它们(你为 WM_PAINT 绘制窗口,你退出你的WM_QUIT 等申请)。

现在假设您有一个工作线程,如何调用您的主线程?最简单的方法是使用这个底层结构来解决问题。我过度简化了任务,但这些是步骤:

  • 创建一个(线程安全的)函数队列来调用(一些示例 here on SO)。
  • 向窗口过程发送自定义消息。如果您将此队列设置为优先级队列,那么您甚至可以决定这些调用的优先级(例如,来自工作线程的进度通知的优先级可能低于警报通知)。
  • 在窗口过程中(在您的 switch/case 语句中)您理解该消息,然后您可以查看要从队列中调用的函数并调用它。

WPF 和 WinForms 都使用此方法将消息从线程传递(调度)到 UI 线程。看看this article on MSDN有关多线程和用户界面的更多详细信息,WinForms 隐藏了很多这些详细信息,您不必关心它们,但您可以看一下以了解它在后台的工作原理。

关于c# - 如何从另一个线程调用 UI 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10170448/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com