C# 如何从另一个线程调用UI方法
玩计时器。 上下文:带有两个标签的winforms 我想看看C# 如何从另一个线程调用UI方法,c#,.net,multithreading,winforms,timer,C#,.net,Multithreading,Winforms,Timer,玩计时器。 上下文:带有两个标签的winforms 我想看看System.Timers.Timer是如何工作的,所以我没有使用表单计时器。 我知道表单和myTimer现在将在不同的线程中运行。 是否有一种简单的方法可以用以下形式表示lblValue上经过的时间 我已经看过了,但有没有更简单的方法 以下是winforms代码: using System.Timers; namespace Ariport_Parking { public partial class AirportParkin
System.Timers.Timer
是如何工作的,所以我没有使用表单计时器。
我知道表单和myTimer现在将在不同的线程中运行。
是否有一种简单的方法可以用以下形式表示lblValue
上经过的时间
我已经看过了,但有没有更简单的方法
以下是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(); }
}
}
}
使用系统定时器;
停车场
{
公共部分类机场停车场:表格
{
//表单的实例变量
System.Timers.Timer myTimer;
int ElapsedCounter=0;
int MaxTime=5000;
int elapsedTime=0;
静态长度=100;
公共机场停车场
{
初始化组件();
保持时间();
lblValue.Text=“你好”;
}
//计时方法
公共无效保留时间(){
myTimer=新系统.Timers.Timer(滴答声长度);
myTimer.appeased+=新的ElapsedEventHandler(myTimer\u appeased);
myTimer.AutoReset=true;
myTimer.Enabled=true;
myTimer.Start();
}
void myTimer_已过(对象myObject、事件args myEventArgs){
myTimer.Stop();
ElapsedCounter+=1;
elapsedTime+=滴答声长度;
if(elapsedTime
我想您的代码只是一个测试,所以我不会讨论您如何使用计时器。这里的问题是如何在计时器回调中使用用户界面控件
大多数控件的方法和属性只能从UI线程访问(实际上只能从创建它们的线程访问它们,但这是另一回事)。这是因为每个线程都必须有自己的消息循环(GetMessage()
按线程过滤消息),然后要使用控件
执行操作,必须将消息从线程分派到主线程。在.NET中,这很容易,因为每个控件
为此继承了两个方法:Invoke/BeginInvoke/EndInvoke
。要知道执行线程是否必须调用这些方法,您需要属性invokererequired
。只需使用以下命令更改代码即可:
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";
}));
}
请注意,当前线程将阻塞,直到UI线程完成方法执行。如果线程的计时很重要,这可能是一个问题(不要忘记UI线程可能会很忙或挂起一点)。如果您不需要方法的返回值,只需将Invoke
替换为BeginInvoke
,对于WinForms,您甚至不需要后续调用EndInvoke
:
void DoStuff() {
if (InvokeRequired) {
BeginInvoke(new MethodInvoker(DoStuff));
} else {
// Do things
}
}
如果需要返回值,则必须处理通常的IAsyncResult
接口
它是如何工作的?
GUI Windows应用程序基于窗口过程及其消息循环。如果您使用纯C编写应用程序,则会出现如下情况:
MSG message;
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
使用这几行代码,应用程序等待消息,然后将消息传递给窗口过程。窗口过程是一个大的开关/案例语句,您可以检查您知道的消息(WM\u
),并以某种方式处理它们(为WM\u-paint
绘制窗口,为WM\u-quit
退出应用程序等等)
现在假设您有一个工作线程,如何调用主线程?最简单的方法是使用这个底层结构来完成这个任务。我过分简化了任务,但步骤如下:
- 创建一个(线程安全的)要调用的函数队列(一些示例)
- 将自定义消息发布到窗口过程。如果将此队列设为优先级队列,则您甚至可以决定这些调用的优先级(例如,来自工作线程的进度通知的优先级可能低于报警通知)
- 在窗口过程(在switch/case语句中)中,您了解该消息,然后可以从队列中查看要调用的函数并调用它
WPF和WinForms都使用此方法将消息从线程传递(分派)到UI线程。查看以了解有关多线程和用户界面的更多详细信息,WinForms隐藏了许多这些详细信息,您不必关心它们,但您可以查看以了解它是如何在后台工作的。首先,在Windows窗体(和大多数框架)中,只能访问控件(除非记录为“线程安全的”)通过UI线程
因此,回调中的this.lblElapsedTime.Text=…
显然是错误的。看一看
第二,你应该使用和来计算你的时间
未经测试:
DateTime startTime = DateTime.Now;
void myTimer_Elapsed(...) {
TimeSpan elapsed = DateTime.Now - startTime;
this.lblElapsedTime.BeginInvoke(delegate() {
this.lblElapsedTime.Text = elapsed.ToString();
});
}
正如被问到的,这里是我的答案,检查跨线程调用,同步变量更新,不停止和启动计时器,也不使用计时器计算经过的时间 编辑修复了
BeginInvoke
呼叫。我已经使用一个通用的操作完成了跨线程调用,这允许传递发送方和eventargs。如果未使用这些方法(如此处所示),则使用MethodInvoker
更有效,但我怀疑需要将处理移到无参数方法中
public partial class AirportParking : Form
{
private Timer myTimer = new Timer(100);
private int elapsedCounter = 0;
private readonly DateTime startTime = DateTime.Now;
private const string EvenText = "hello";
private const string OddText = "hello world";
public AirportParking()
{
lblValue.Text = EvenText;
myTimer.Elapsed += MyTimerElapsed;
myTimer.AutoReset = true;
myTimer.Enabled = true;
myTimer.Start();
}
private void MyTimerElapsed(object sender,EventArgs myEventArgs)
{
If (lblValue.InvokeRequired)
{
var self = new Action<object, EventArgs>(MyTimerElapsed);
this.BeginInvoke(self, new [] {sender, myEventArgs});
return;
}
lock (this)
{
lblElapsedTime.Text = DateTime.Now.SubTract(startTime).ToString();
elapesedCounter++;
if(elapsedCounter % 2 == 0)
{
lblValue.Text = EvenText;
}
else
{
lblValue.Text = OddText;
}
}
}
}
公共部分类机场停车场:表格
{
专用定时器myTimer=新定时器(100);
私有内部elapsedCounter=0;
private readonly DateTime startTime=DateTime.Now;
private const string EvenText=“hello”;
private const string OddText=“hello world”;
公共机场停车场
{
lblValue.Text=EvenText;
myTimer.Appeased+=MyTimeRecursed;
public partial class AirportParking : Form
{
private Timer myTimer = new Timer(100);
private int elapsedCounter = 0;
private readonly DateTime startTime = DateTime.Now;
private const string EvenText = "hello";
private const string OddText = "hello world";
public AirportParking()
{
lblValue.Text = EvenText;
myTimer.Elapsed += MyTimerElapsed;
myTimer.AutoReset = true;
myTimer.Enabled = true;
myTimer.Start();
}
private void MyTimerElapsed(object sender,EventArgs myEventArgs)
{
If (lblValue.InvokeRequired)
{
var self = new Action<object, EventArgs>(MyTimerElapsed);
this.BeginInvoke(self, new [] {sender, myEventArgs});
return;
}
lock (this)
{
lblElapsedTime.Text = DateTime.Now.SubTract(startTime).ToString();
elapesedCounter++;
if(elapsedCounter % 2 == 0)
{
lblValue.Text = EvenText;
}
else
{
lblValue.Text = OddText;
}
}
}
}
using System.Timers;
namespace Ariport_Parking
{
public partial class AirportParking : Form
{
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//instance variables of the form
System.Timers.Timer myTimer;
private const string EvenText = "hello";
private const string OddText = "hello world";
static int tickLength = 100;
static int elapsedCounter;
private int MaxTime = 5000;
private TimeSpan elapsedTime;
private readonly DateTime startTime = DateTime.Now;
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public AirportParking()
{
InitializeComponent();
lblValue.Text = EvenText;
keepingTime();
}
//method for keeping time
public void keepingTime() {
using (System.Timers.Timer myTimer = new System.Timers.Timer(tickLength))
{
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
myTimer.AutoReset = true;
myTimer.Enabled = true;
myTimer.Start();
}
}
private void myTimer_Elapsed(Object myObject,EventArgs myEventArgs){
elapsedCounter++;
elapsedTime = DateTime.Now.Subtract(startTime);
if (elapsedTime.TotalMilliseconds < MaxTime)
{
this.BeginInvoke(new MethodInvoker(delegate
{
this.lblElapsedTime.Text = elapsedTime.ToString();
if (elapsedCounter % 2 == 0)
this.lblValue.Text = EvenText;
else
this.lblValue.Text = OddText;
}));
}
else {myTimer.Stop();}
}
}
}
private void InvokeUI(Action a)
{
this.BeginInvoke(new MethodInvoker(a));
}
InvokeUI(() => {
Label1.Text = "Super Cool";
});