[英]C# SynchronizingObject and Timer.Timer thread
嗨,我想在我的主線程上使Timer.Timer類的Elapsed事件發生。 我被限制在VS 2008,.net 3.5 ......在這篇文章中: C#Timers是否在一個單獨的線程上過去了? 據說使用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 ......
這是為什么? 如何在mainthread上執行Elapsed處理程序。
我嘗試將SynchronizingObject用於事件,但這也沒有幫助:
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或BeginInvoke。”
最后的選擇是: http : //www.codeproject.com/Articles/12082/A-DelegateQueue-Class ? msg = 3655119#xxx3655119xx但是真的有必要嗎? 還是有一些更簡單的解決方案?
我找到了一個使用DispatcherTimer和Dispatcher.Run()的解決方案來在新線程中啟動消息泵。 為了停止調度員並順利退出服務,我使用了
if (_currentDispatcher != null) _currentDispatcher.InvokeShutdown();
_thread.Join();
建議的答案沒有回答原始問題,這就是為什么System.Timers.Timer Elapsed事件沒有在主線程上正確觸發,即使SynchronizingObject正確地設置為主線程的控件。
我自己遇到了這個問題,事實證明解決方案是因為Control還沒有手柄,即使它已經正確構建了。 訣竅是將Control的Handle屬性的值轉換為虛擬變量,以便初始化它(參見http://blogs.msdn.com/b/jfoscoding/archive/2004/11/24/269416.aspx )
但是一旦問題得到解決,它就會在原始海報的代碼中產生一個新問題,因為現在存在死鎖,因為Console.ReadKey()正在阻塞但是Elapsed處理程序需要在該線程上運行。 一個解決方案是執行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 class
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.