使用Visual Studio 2015,可视化C#windows应用程序。
我需要一个计时器来清空主线程中的队列,因为某些UI组件已被修改。我编码了这个:
public Form1()
{
InitializeComponent();
Q = new ConcurrentQueue<amsg>();
timer = new System.Timers.Timer(100);
timer.Elapsed += timer_Elapsed;
timer.Enabled = true;
}
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
clsUdpMsg msg;
while (Q.TryDequeue(out msg)) handleRx(msg);
}
但是Elapsed事件在工作线程中执行......?
如果我在设计时在主窗体上放置一个计时器,生成的原型会略有不同:
private void timer1_Tick(object sender, EventArgs e)
{
amsg msg;
while (Q.TryDequeue(out msg)) handleRx(msg);
}
但是这一个在主线程上下文中执行。
问题扩展到以这种方式创建计时器的任何线程;我认为在线程中创建的计时器在同一个线程上下文中执行elapsed事件。我错过了什么吗?
快速编辑:我看到这些是不同的计时器,System.Timers.Timer和Windows.Forms.Timer。那么 - 我如何可靠地创建将在我创建它们的线程中执行的计时器?
答案 0 :(得分:4)
是的,你只是处理两个单独的Timer
类。 System.Windows.Forms.Timer
在主线程上触发其Tick
事件,System.Timers.Timer
在后台线程上触发其Elapsed
事件。 System.Windows.Forms.Timer
的优点是它隐藏了线程; System.Timers.Timer
的优点是它不需要消息泵工作。
你问,“我如何可靠地创建将在我创建它们的线程中执行的计时器?”嗯,当你有一个Windows窗体GUI并在UI线程上启动Timer时,这很容易:只需使用System.Windows.Forms.Timer
。如果你没有消息泵来利用,你必须使用System.Timers.Timer
,并且基本上当计时器触发其Elapsed
事件时在主线程上运行某些东西的唯一方法是重新创建类似于主线程中的消息循环 - 让它坐下,可能等待来自计时器线程的Monitor.Pulse
或其他类型的通知。但这意味着你的主线程在此期间不会得到任何有用的东西......
答案 1 :(得分:1)
这不是您问题的直接答案,而是避免计时器线程问题的建议。
您可以随时使用Microsoft的Reactive Framework(NuGet“Rx-WinForms”)。
您的代码如下所示:
var subject = new Subject<clsUdpMsg>();
var subscription =
subject
.ObserveOn(this)
.Subscribe(msg => handleRx(msg));
subject
取代Q
。每当您想要添加消息时,只需拨打subject.OnNext(msg)
。
即使在后台线程上调用.ObserveOn(this)
,subject.OnNext(msg)
也会将回调调回UI。
如果您想停止订阅,只需致电subscription.Dispose()
。