您好,我正在尝试使Timer.Timer类的Elapsed事件在我的主线程上触发。我被限制在VS2008,.net 3.5...在这篇文章中:Do C# Timers elapse on a separate thread?指出,使用SynchronizingObject将使处理程序在拥有该对象的线程上执行。
所以我试了一下:
class MyTimer
{
private readonly Timer timer;
public MyTimer(ISynchronizeInvoke synchronizingObject)
{
Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
timer = new Timer(1000);
timer.SynchronizingObject = synchronizingObject;
timer.Elapsed +=
delegate
{
timer.Stop();
Thread.Sleep(2000);
Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
timer.Start();
};
timer.Start();
}
}
class Program
{
static void Main(string[] args)
{
ISynchronizeInvoke syncObject = new Control();
var mytimer = new MyTimer(syncObject);
Console.ReadKey();
}
}但是输出是: 1,4,4,4,4...
为什么会这样呢?如何让经过的处理程序在主线程上执行。
我试着使用SynchronizingObject for an event,但也没有用:
public static ElapsedEventHandler Wrap(ElapsedEventHandler original, ISynchronizeInvoke synchronizingObject)
{
return (sender, args) =>
{
if (synchronizingObject.InvokeRequired)
{
synchronizingObject.Invoke(original, new object[] { sender, args });
}
else
{
original(sender, args);
}
};
}和:
timer.Elapsed += Wrap(
delegate
{
timer.Stop();
Thread.Sleep(200);
Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
timer.Start();
},
synchronizingObject);但还是没有成功。每次InvokeRequired为假..。
在invoke中强制调试会导致无效操作:“在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。”
最后的办法是查看:http://www.codeproject.com/Articles/12082/A-DelegateQueue-Class?msg=3655119#xx3655119xx,但这真的有必要吗?或者有更简单的解决方案?
发布于 2012-10-18 15:43:37
我找到了一个解决方案,使用DispatcherTimer和Dispatcher.Run()在新线程中启动消息泵。为了停止dispatcher并顺利退出服务,我使用了
if (_currentDispatcher != null) _currentDispatcher.InvokeShutdown();
_thread.Join();发布于 2015-04-25 21:10:38
建议的答案没有回答最初的问题,这就是为什么即使SynchronizingObject被正确设置为来自主线程的控件,System.Timers.Timer Elapsed事件也不能正确地在主线程上激发。
我自己也遇到过这个问题,结果证明解决方案是因为Control还没有句柄,尽管它已经被正确地构造了。诀窍是将控件的Handle属性的值放入一个伪变量中,以便对其进行初始化(请参阅http://blogs.msdn.com/b/jfoscoding/archive/2004/11/24/269416.aspx )
但是,一旦这个问题得到解决,它就会在原始poster的代码中产生一个新问题,因为Console.ReadKey()正在阻塞,而经过的处理程序需要在该线程上运行,所以现在出现了死锁。一种解决方案是使用Application.DoEvents(),尽管可能有更好的解决方案。底线:当同步回主线程时,不要阻塞它!
发帖者的原始代码经过修改后可以正常工作:
class MyTimer {
private readonly System.Timers.Timer timer;
private static AutoResetEvent elapsedOutputted = new AutoResetEvent(false);
//private static AutoResetEvent mainReady = new AutoResetEvent(true);
public MyTimer(ISynchronizeInvoke synchronizingObject) {
Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
timer = new System.Timers.Timer(1000);
timer.SynchronizingObject = synchronizingObject;
timer.Elapsed += timer_Elapsed;
timer.Start();
}
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
//timer.Stop(); not needed
Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
elapsedOutputted.Set();
//Thread.Sleep(2000); not needed
//timer.Start(); not needed
}
static void Main(string[] args) {
Control c = new Control();
IntPtr tempNotUsed = c.Handle;
var mytimer = new MyTimer(c);
for (int runs = 0; runs < 10; runs++) {
while (!elapsedOutputted.WaitOne(1000)) { //this will deadlock, but the 1000ms timeout will free it
Application.DoEvents(); //not sure if DoEvents is the best idea, but it does the trick
} //end while
} //end for
} //end Main
} //end classhttps://stackoverflow.com/questions/12782139
复制相似问题