我想做什么。我想定期调用SomeMethod。因此,我希望计时器将在后台线程方法体传递后从backgroung线程启动。调用了_timer.Start()
,但TickHandler没有;
代码:
using Timer = System.Windows.Forms.Timer;
class TestTimer
{
private Timer _timer;
private Thread _thread;
public TestTimer()
{
// create and initializing timer. but not started!
_timer = new Timer();
_timer.Tick += TickHandler;
_timer.Interval = 60000; // 1 minute
// create and start new thread
_thread = new Thread(SomeMethod);
_thread.Start();
}
private void TickHandler(object sender, EventArgs e)
{
// stop timer
_timer.stop();
//some handling
// run background thread again
_thread = new Thread(SomeMethod);
_thread.Start();
}
private void SomeMethod()
{
// some operations
// start timer!
TimerStart();
}
private void TimerStart()
{
_timer.Start();
}
}
通过猴子方法,我发现如果像这样添加委托
internal delegate void TimerDelegate();
并替换字符串
TimerStart();
带
Application.Current.Dispatcher.Invoke(new TimerDelegate(TimerStart), null);
一切正常。有人可以解释一下诀窍是什么?
答案 0 :(得分:4)
你的事情有些混乱。
如果你想在后台线程上触发的计时器,你不必创建一个线程来启动它(哪个线程调用无关紧要) Start
方法)。只需使用System.Timers.Timer
,每个Elapsed
事件都将发生在线程池线程上。
如果你想在 UI线程上触发的计时器,因为看起来你正在使用WPF,你应该使用System.Windows.Threading.DispatcherTimer
,而不是您一直在使用的Windows窗体计时器。您应该创建特定UI线程上的计时器(即调用new
),并且每个Tick
事件都将在该线程上发生。同样,您调用Start
的哪个主题无关紧要。
以下是对代码中发生的事情的解释:您正在非UI线程上启动Windows窗体计时器。这种计时器需要在该线程上运行消息泵,以便它可以接收消息。因为它是一个非UI线程,所以没有消息泵。当您使用Dispatcher.Invoke
方法时,您将计时器的创建封送回应用程序的主UI线程,这使其工作。但这完全是多余的。如果您希望保持代码不变,只需将计时器替换为DispatcherTimer
,然后您就可以删除Invoke
来电。
或者,如果您使用的是.NET 4.5,则可以使用await / async来使这一切变得更加容易(请务必从UI线程调用SomeMethod
):
async Task SomeMethod(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await Task.Run(() => DoAsyncStuff(), ct);
DoUIStuff();
await Task.Delay(TimeSpan.FromMinutes(1), ct);
}
}
答案 1 :(得分:0)
MSDN可以为您解释:
注意Windows窗体计时器组件是单线程的,并且是 限制精度为55毫秒。如果你需要 多线程计时器具有更高的准确性,使用Timer中的类 System.Timers名称空间。