gpt4 book ai didi

c# - 如何从使用计时器的对象引发事件返回到 UI 线程

转载 作者:行者123 更新时间:2023-11-30 15:40:13 25 4
gpt4 key购买 nike

我有一个对象,它使用计时器偶尔轮询资源,然后在轮询发现值得注意的内容时引发事件。我已经查看了其他几个示例,但似乎找不到一种方法来将事件编码回 UI 线程,而无需在 UI 线程的事件处理程序上添加额外的代码。所以我的问题是:

有什么方法可以对我的对象的用户隐藏这种额外的努力吗?

为了讨论的目的,我将包括一个简单的例子:

假设我有一个带有 1 个富文本框的表单:

private void Form1_Load(object sender, EventArgs e)
{
var listener = new PollingListener();
listener.Polled += new EventHandler<EventArgs>(listener_Polled);
}

void listener_Polled(object sender, EventArgs e)
{
richTextBox1.Text += "Polled " + DateTime.Now.Second.ToString();
}

我还有这个对象:

public class PollingListener
{
System.Timers.Timer timer = new System.Timers.Timer(1000);
public event EventHandler<EventArgs> Polled;
public PollingListener()
{
timer.Elapsed +=new System.Timers.ElapsedEventHandler(PollNow);
timer.Start();
}

void PollNow(object sender, EventArgs e)
{
var temp = Polled;
if (temp != null) Polled(this, new EventArgs());

}
}

如果我按预期运行它,它会产生异常

"Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on"

这对我来说很有意义,我可以这样包装事件处理程序方法:

void listener_Polled(object sender, EventArgs e)
{
this.BeginInvoke(new Action(() => { UpdateText() }));
}
void UpdateText()
{
richTextBox1.Text += "Polled " + DateTime.Now.Second.ToString();
}

但是现在我的对象的用户必须对从我的控件中的计时器事件引发的任何事件执行此操作。那么,有什么我可以添加到我的 PollingListener 类中的,它不会更改它的方法的签名以传递额外的引用,从而允许我的对象的用户忘记后台到 UI 线程的编码(marshal)处理事件吗?

感谢您的任何意见。

最佳答案

评论后添加:

您需要获取一些可以利用的潜在细节以实现该目标。

我想到的一件事是在构造时创建您自己的 Forms/WPF 计时器,然后使用它和一些同步来隐藏跨线程协调的细节。我们可以从您的样本中推断,您的轮询器的构造应该始终发生在您的消费者线程的上下文中。

这是一种相当 hack-ish 的方式来完成你想要的,但它可以完成任务,因为你的轮询监听器的构造是从消费者的线程发生的(它有一个 Windows 消息泵来插入 Forms/WPF 计时器),以及该类的其余操作可以从任何线程发生,因为窗体计时器的滴答将从原始线程发出心跳。 正如其他评论和答案所指出的,最好重新评估和修复您的投票操作与消费者之间的操作关系

这是该类的更新版本 PollingListener2,它使用 ManualResetEvent 和隐藏的 System.Windows.Forms.Timer 跨线程传送轮询通知。为简洁起见,省略了清理代码。建议在此类的生产版本中要求使用 IDisposable 进行显式清理。

ManualResetEvent @ MSDN

public class PollingListener2
{
System.Timers.Timer timer = new System.Timers.Timer(1000);
public event EventHandler<EventArgs> Polled;

System.Windows.Forms.Timer formsTimer;
public System.Threading.ManualResetEvent pollNotice;

public PollingListener2()
{
pollNotice = new System.Threading.ManualResetEvent(false);
formsTimer = new System.Windows.Forms.Timer();
formsTimer.Interval = 100;
formsTimer.Tick += new EventHandler(formsTimer_Tick);
formsTimer.Start();
timer.Elapsed += new System.Timers.ElapsedEventHandler(PollNow);
timer.Start();
}

void formsTimer_Tick(object sender, EventArgs e)
{
if (pollNotice.WaitOne(0))
{
pollNotice.Reset();
var temp = Polled;
if (temp != null)
{
Polled(this, new EventArgs());
}
}
}

void PollNow(object sender, EventArgs e)
{
pollNotice.Set();
}
}

这在遥远的 Win32 过去有一些先例,有些人会使用隐藏窗口等来保持一只脚在另一个线程中,而不需要消费者对他们的代码进行任何重大更改(有时不需要更改)。


原文:

您可以在类型为 ControlForm 的辅助类中添加一个成员变量,并将其用作 BeginInvoke() 的作用域/Invoke() 调用您的事件调度。

这是您的示例类的副本,已修改为以这种方式运行。

public class PollingListener
{
System.Timers.Timer timer = new System.Timers.Timer(1000);
public event EventHandler<EventArgs> Polled;

public PollingListener(System.Windows.Forms.Control consumer)
{
timer.Elapsed += new System.Timers.ElapsedEventHandler(PollNow);
timer.Start();
consumerContext = consumer;
}

System.Windows.Forms.Control consumerContext;

void PollNow(object sender, EventArgs e)
{
var temp = Polled;
if ((temp != null) && (null != consumerContext))
{
consumerContext.BeginInvoke(new Action(() =>
{
Polled(this, new EventArgs());
}));
}
}
}

下面是一个演示此操作的示例。在 Debug模式下运行它并查看您的输出以验证它是否按预期工作。

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

listener = new PollingListener(this);
}

PollingListener listener;

private void Form1_Load(object sender, EventArgs e)
{
listener.Polled += new EventHandler<EventArgs>(listener_Poll);
}

void listener_Poll(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("ding.");
}
}

关于c# - 如何从使用计时器的对象引发事件返回到 UI 线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9525467/

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