I have a question about System.Windows.Forms.Timer. Is it possible to get Tick event after disposing it? For example, if the message is in the message loop and I dispose the timer meanwhile. If it is possible what is the best way to prevent against it. Do you now any good sources explaining it, because I couldn't find anything explaining it. Here is same code explaining my problem:
namespace TestTimer
{
public partial class Form1 : Form
{
ObjectWithTimer obj = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if(obj != null)
{
obj.Deinit();
obj = null;
}
obj = new ObjectWithTimer();
}
}
public class ObjectWithTimer
{
public Object o = new object();
public Timer timer = new Timer();
bool disposed = false;
public ObjectWithTimer()
{
timer.Interval = 10;
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;
}
public void Deinit()
{
timer.Enabled = false;
timer.Tick -= new EventHandler(timer_Tick);
timer.Dispose();
timer = null;
disposed = true;
}
private void timer_Tick(object sender, EventArgs e)
{
if (disposed)
{
//Is it possible to get here
if (timer.Enabled) return;
}
//doing something
}
}
}
Understanding how timers work can help you feel better about it. They are implemented by the operating system, the underlying winapi call to start a timer is SetTimer(). The OS then posts a notification whenever the timer ticks, you get a WM_TIMER message. The plumbing in Winforms ensures that your Tick event handler runs when this message is received.
These messages are stored in the message queue, an internal data structure associated with a window. This queue serializes messages, it is the basic mechanism that ensures that you for example can never lose a mouse click or a keyboard key press, even when the window is unresponsive because the UI thread is busy with something else.
This queue gives reason to be cautious, what happens when the queue stores a WM_TIMER message when you disposed the timer? Unless something drastic is done, you'd still get that message and your Tick event handler will fire.
But no need to worry, WM_TIMER belongs to a small group of messages that are generated in a special way. They are synthesized messages, it is only ever generated when your program asks for a message with GetMessage(). Other common messages that belong that group are WM_PAINT, it fires the Paint event. Note how you can call Invalidate() as often as you like, you still get only a single Paint event. WM_MOUSEMOVE is another one, it fires the MouseMove event. Something you can reason about, no matter how fast you move the mouse, you can never flood the message queue with mouse-move messages.
Another characteristic of these synthesized messages is that they appear to have a "low priority". Given is that they are only ever synthesized when the message queue is empty. Which is why keyboard messages and mouse clicks always generate an event ahead of a paint.
Long story short, you can only get a WM_TIMER message if you ask for a message and the timer is still alive. The Timer.Dispose() method calls KillTimer() under the hood. Which ends any opportunity to still get a Tick event. Only possible way that could get screwed up is when you call the Stop() or Dispose() methods from a worker thread. Don't do that.
The Windows Forms Timer is single threaded so is not possible that while disposing it you are in timer_Tick.
Also you are not detaching your event in deinit function.
This is very easy to test. I've modified your code a bit:
public class Form1 : Form
{
public Form1()
{
var button = new Button();
button.Click += button1_Click;
Controls.Add(button);
}
private void button1_Click(object sender, EventArgs e)
{
var obj = new ObjectWithTimer();
Thread.Sleep(2000);
obj.Deinit();
}
}
public class ObjectWithTimer
{
public System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
bool disposed = false;
public ObjectWithTimer()
{
timer.Interval = 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;
}
public void Deinit()
{
timer.Enabled = false;
timer.Tick -= new EventHandler(timer_Tick);
timer.Dispose();
timer = null;
disposed = true;
}
private void timer_Tick(object sender, EventArgs e)
{
"Ticked".Dump();
}
}
The Thread.Sleep ensures the UI thread is occupied while the timer does its ticking.
The result? No, the Tick will not fire after the timer is disabled. Even the timer.Tick -= new EventHandler(timer_Tick); is unnecessary.
Related
While I using winforms app, each 5 minute I need to check data updates. I need to send request to few service then get response and update data in database. What's is the best practices to make on another thread (or task ?)? The program should not slow down.
I try to make with timer:
Init timer when program is running
public class Timer
{
private System.Timers.Timer timer;
private void InitTimer()
{
timer = new System.Timers.Timer(4000);
timer.Elapsed += ElapsedTime;
timer.Enabled = true;
}
private void ElapsedTime()
{
//send request and update data
}
}
The way you are doing it will work just fine. The documentation for Sytem.Timers.Timer says:
If the SynchronizingObject property is null, the Elapsed event is raised on a ThreadPool thread.
The SynchronizingObject property is null by default, so your Elasped event will run on a ThreadPool thread, not on the UI thread. That means it will not stop your application from responding to user input.
If there is a chance that ElapsedTime() will run longer than your interval, and you don't want the events overlapping, then you can set AutoReset to false and reset it manually at the end of ElapsedTime(). Just make sure that everything is wrapped in a try/catch block, otherwise the timer won't get reset if there's an exception. My code below shows how that would look.
You don't need to use async/await anywhere here. Since it won't be running on the UI thread, using asynchronous code won't really help you any. In a desktop app, it's not a big deal to have a separate (non-UI) thread wait.
public class Timer
{
private System.Timers.Timer timer;
private void InitTimer()
{
timer = new System.Timers.Timer(4000);
timer.Elapsed += ElapsedTime;
timer.AutoReset = false;
timer.Enabled = true;
}
private void ElapsedTime()
{
try {
//send request and update data
}
catch (Exception e)
{
//log the error
}
finally
{
//start the timer again
timer.Enabled = true;
}
}
}
I'm having trouble with the following code. I have some code that calls SetTimer() and expects the user to respond before interval is reached (in
millisecs). The calling code inherit these funtions. If the user responds, then StopTimer() is called, info is displayed, StartTimer() is called, and the user is expected to respond again within the interval time period. This continues until the user fails in an answer or takes too long (goes past the interval).
The problem is the timers don't stop. They keep repeating even after I've stopped them, set their Tick event to null (by the -= method), and left its scope. I even get new storage with a new DispatcherTimer (I've done this both using the old one and a new one each time). I can't get the old Timer to go away.
What am I doing wrong?
using Windows.UI.XAML;
public DispatcherTimer GameTimer;
internal void SetTimer(int interval)
{
GameTimer = new DispatcherTimer();
GameTimer.Tick += TimerCallback;
GameTimer.Interval = new TimeSpan(0,0,0,0,interval);
GameTimer.Start();
}
internal void StopTimer()
{
GameTimer.Stop();
try
{
GameTimer.Tick -= TimerCallback;
} catch {}
}
private void TimerCallback(object sender, object e)
{
StopTimer();
// Other code
}
Thanks in advance,
-justin
Try stopping the timer by using the sender object, not the actual public timer object:
private void TimerCallback(object sender, object e) {
(sender as DispatcherTimer).Stop();
// Other code
}
As a workaround, you could do something like:
// in your class
private bool _allowExecution = false;
Then whenever you start the time set _allowExecution = true; and when you stop the timer, simply add _allowExecution = false;
The last thing will be to add a simply boolean condition on your timer execute: if (_allowExecute) //do your stuff here
Because you initialize a new DispatcherTimer everytime call SetTimer(int interval). You must stop the old DispatcherTimer instance before initialize a new one.
internal void SetTimer(int interval)
{
StopTimer();
GameTimer = new DispatcherTimer();
GameTimer.Tick += TimerCallback;
GameTimer.Interval = new TimeSpan(0,0,0,0,interval);
GameTimer.Start();
}
internal void StopTimer()
{
if(GameTimer != null)
{
GameTimer.Stop();
GameTimer.Tick -= TimerCallback;
GameTimer = null;
}
}
I have an object that uses a timer to occasionally poll for a resource and then raises an event whenever the poll finds something of note. I have looked at several other examples but can't seem to find a method to marshall the event back to the UI thread without extra code on the event handler on the UI thread. So my question is:
Is there any way to hide this extra effort from the users of my object?
For the purpose of discussion I will include a trivial example:
Imagine I have a form with 1 richtextbox:
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();
}
Also I have this object:
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());
}
}
If I run this, as expected it yields the exception
"Cross-thread operation not valid: Control 'richTextBox1' accessed
from a thread other than the thread it was created on"
This makes sense to me, and I can wrap the event handler method differently as so:
void listener_Polled(object sender, EventArgs e)
{
this.BeginInvoke(new Action(() => { UpdateText() }));
}
void UpdateText()
{
richTextBox1.Text += "Polled " + DateTime.Now.Second.ToString();
}
But now the user of my object has to do this for any event that is raised from the timer event in my control. So, is there anything I can add to my PollingListener class that doesn't change the signature of it's methods to pass in extra references that would allow the user of my object to be oblivious of the marshaling event in the background to the UI thread?
Thanks for any input you may have.
Added after comment:
You would need to pickup some latent detail that you can exploit to be able to accomplish that goal.
One thing that comes to mind is creating your own Forms/WPF timer at construction time and then use this and some synchronization to hide the details of coordination across threads. We can infer from your sample that construction of your poller should always happen in context of your consumer's thread.
This is a rather hack-ish way to accomplish what you want, but it can accomplish the deed because the construction of your poll-listener happens from the consumer's thread (which has a windows message pump to fuel the dispatches of Forms/WPF timers), and the rest of the operation of the class could occur from any thread as the forms Timer's tick will heartbeat from the original thread. As other comments and answers have noted, it would be best to reassess and fix the operating relationship between your polling operations and the consumer.
Here is an updated version of the class, PollingListener2 that uses a ManualResetEvent and a concealed System.Windows.Forms.Timer to ferry the polling notice across threads. Cleanup code is omitted for the sake of brevity. Requiring the use of IDisposable for explicit cleanup would be recommended in a production version of this class.
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();
}
}
This has some precedent in the distant Win32 past where some people would use hidden windows and the like to maintain one foot in the other thread without requiring the consumer to make any significant changes to their code (sometimes no changes are necessary).
Original:
You could add a member variable on your helper class of type Control or Form and use that as the scope for a BeginInvoke() / Invoke() call on your event dispatch.
Here's a copy of your sample class, modified to behave in this manner.
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());
}));
}
}
}
Here's a sample that shows this in action. Run this in debug mode and look at your output to verify that it is working as expected.
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.");
}
}
If the processing work inside your PollNow is fairly small then you do not need to perform it on a separate thread. If WinForms use Timer, in WPF you use DispatchTimer and then you are performing the test on the same thread as the UI and there is no cross-thread issue.
This SO question prompted this comment:
I think this excerpt is enlightening: "Unlike the
System.Windows.Forms.Timer, the System.Timers.Timer class will, by
default, call your timer event handler on a worker thread obtained
from the common language runtime (CLR) thread pool. [...] The
System.Timers.Timer class provides an easy way to deal with this
dilemma—it exposes a public SynchronizingObject property. Setting this
property to an instance of a Windows Form (or a control on a Windows
Form) will ensure that the code in your Elapsed event handler runs on
the same thread on which the SynchronizingObject was instantiated."
And System.Times.Timer doc says of SynchronizingObject:
Gets or sets the object used to marshal event-handler calls that are
issued when an interval has elapsed.
Both of which implie that if you pass a control created on the UI thread as the sync object then the timer will effectively marshal the timer event calls to the UI thread.
I have a statusbar label and I want to show a text on my StatusBar Label for 3 seconds only
How can I do it without using threads?
public void InfoLabel(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(InfoLabel), new object[] { value });
return;
}
infoLabel.Text = value;
}
Simply add timer on the end of your method:
if (!string.IsNullOrWhiteSpace(value))
{
System.Timers.Timer timer = new System.Timers.Timer(3000) { Enabled = true };
timer.Elapsed += (sender, args) =>
{
this.InfoLabel(string.Empty);
timer.Dispose();
};
}
You need to define a function that you call each time you need to display your text, inside this function you define a timer, this timer is based on System.Windows.Forms.Timer, the only difference is that its modified to hold a stopTime parameter that represents the running duration, the only thing you need to do is to put your starting code(display text) inside the MyFunction function and to put the ending code(to stop displaying text) inside the Timer_Tick function, once you call MyFunction just specify how many seconds you want it to run in the function parameter.
private void MyFunction(int durationInSeconds)
{
MyTimer timer = new MyTimer();
timer.Tick += new EventHandler(Timer_Tick);
timer.Interval = (1000) * (1); // Timer will tick every second, you can change it if you want
timer.Enabled = true;
timer.stopTime = System.DateTime.Now.AddSeconds(durationInSeconds);
timer.Start();
//put your starting code here
}
private void Timer_Tick(object sender, EventArgs e)
{
MyTimer timer = (MyTimer)sender;
if (System.DateTime.Now >= timer.stopTime)
{
timer.Stop();
//put your ending code here
}
}
the modified timer class
public class MyTimer : System.Windows.Forms.Timer
{
public System.DateTime stopTime;
public MyTimer()
{
}
}
You can use Timer to create an instance of a timer that waits for n seconds before firing the Elapsed event. In the elapsed event, you clear the label's Content.
As the timer is executed in a separate thread, the UI thread is not locked while the timer is counting i.e. you are free to perform other operations in the UI.
private delegate void NoArgDelegate();
private void StartTimer(int durationInSeconds)
{
const int milliSecondsPerSecond = 1000;
var timer = new Timer(durationInSeconds * milliSecondsPerSecond);
timer.Start();
timer.Elapsed += timer_Elapsed;
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
var clearLabelTextDelegate = new NoArgDelegate(ClearLabelText);
this.Dispatcher.BeginInvoke(clearLabelTextDelegate);
}
private void ClearLabelText()
{
this.myLabel.Content = string.Empty;
}
As I do not the rest of your code, some suggestions would be to create a lock on the timer so as to prevent more than one UI event starting the timer. In addition, the delegate and the timer instance can be made as private members of the class.
You'll always be using at least the GUI thread. If you decide to wait on that thread, no other interaction with controls is possible (ie. no buttons will work, the window will not be repainted).
Alternatively you could use a System.Windows.Forms.Timer that gives control back to the OS, or another type of timer. Either way, the "countdown" will either block user interaction or happen on another thread (under the hood).
I've been trying to get the logic right for my timer and backgroundworker thread. Granted I don't fully understand the whole system despite all my reading. the following are excerpts of code concerned:
My polling button :
private void pollStart_Click(object sender, EventArgs e)
{
tst_bgw = new BackgroundWorker();
//mandatory. Otherwise will throw an exception when calling ReportProgress method
tst_bgw.WorkerReportsProgress = true;
//mandatory. Otherwise we would get an InvalidOperationException when trying to cancel the operation
tst_bgw.WorkerSupportsCancellation = true;
tst_bgw.DoWork += tst_bgw_DoWork;
tst_bgw.ProgressChanged += tst_bgw_ProgressChanged;
tst_bgw.RunWorkerCompleted += tst_bgw_RunWorkerCompleted;
tst_bgw.RunWorkerAsync();
}
which I think is right so far
my Background worker thread:
private void tst_bgw_DoWork(object source, DoWorkEventArgs e)
{
m_timer = new System.Timers.Timer();
m_timer.Interval = 1000;
m_timer.Enabled = true;
m_timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
if (tst_bgw.CancellationPending)
{
e.Cancel = true;
return;
}
}
and the elapsed tier event code:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (powerVal > 3250)
{
m_timer.Stop();
tst_bgw.CancelAsync();
}
else
{
string pow;
int progressVal = 100 - ((3250 - powerVal) / timerVal);
uiDelegateTest tstDel = new uiDelegateTest(recvMessage);// the recvMessage function takes a textbox as an argument and directs output from socket to it.
pow = construct_command("power", powerVal);
sData = Encoding.ASCII.GetBytes(pow);
if (active_connection)
try
{
m_sock.Send(sData);
Array.Clear(sData, 0, sData.Length);
tstDel(ref unit_Output);// Read somewhere that you can only modify UI elements in this method via delegate so I think this is OK.
m_sock.Send(time_out_command);
tstDel(ref unit_Output);
tst_bgw.ReportProgress(progressVal);
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
tst_bgw.ReportProgress(powerVal, progressVal);
powerVal = powerVal + pwrIncVal;
}
I'd just like to know a few other things; am I using the right timer (not that I think it should matter greatly but it was suggested that this might be the best timer for what I want to do) and canI really modify UI elements in the DoWork method only through delegates and if yes are there sepcial considerations to doing so.
Sorry about the long posting and thank you for your time.
There is lots wrong with this code.
1) You aren't disposing of your background worker. BackgroundWorkers must be disposed of after use. They are designed to be used as winforms components and would normally be added to a window via the designer. This will ensure it is created with the form and disposed of when the form is.
2) All you are doing in your dowork method is creating a new timer and running it. There is no point of doing this in a background worker because it will happen so quickly anyway.
3) You will recreate the timer every time you run the background worker again. But you aren't ever stopping or disposing of the old timer, you are just overwriting the member.
I recommend you get rid of the BackgroundWorker completely and just use a timer. Create the timer in the forms constructor and make sure you dispose of it in the forms dispose method. (Or use the designer to add it to the form). In the pollstart_click method just start the timer. (If you have a poll stop method, you can stop the timer in that)
You don't need both a BackgroundWorker and a Timer to accomplish your goal. From what you have posted it looks like you want to have the user click a button which starts a polling process that quits at a certian point.
Your polling model really suggests a timer would work just fine.
If you use a Timer I would Initialize the timer after the InitializeComponent() call with something like
private void InitializeTimer()
{
this.timer = new Timer();
int seconds = 1;
this.timer.Interval = 1000 * seconds; // 1000 * n where n == seconds
this.timer.Tick += new EventHandler(timer_Tick);
// don't start timer until user clicks Start
}
The button_click will simply
private void button_Click(object sender, EventArgs e)
{
this.timer.Start();
}
Then on the timer_Tick you will need to do your polling and you should be able to update your UI from there if the timer is on the UI thread like this
void timer_Tick(object sender, EventArgs e)
{
if( determineIfTimerShouldStop() )
{
this.timer.Stop();
}
else
{
// write a method to just get the power value from your socket
int powerValue = getPowerValue();
// set progressbar, label, etc with value from method above
}
}
However if the timer thread is not on the same thread as the UI you well get an exception while trying to update the UI. In that case you can use the Invoke that DataDink mentions and do something like this
void timer_Tick(object sender, EventArgs e)
{
if( determineIfTimerShouldStop() )
{
this.timer.Stop();
}
else
{
// write a method to just get the power value from your socket
int powerValue = getPowerValue();
// set a label with Invoke
mylabel.Invoke(
new MethodInvoker( delegate { mylabel.Text = "some string"; } )
);
}
}
Given the code you posted you really didn't need to do both a BackgroundWorker and a Timer, but I have had instances where I have used a BackgroundWorker to do work when a timer is called so that I could have a timer update UI periodically and have a manual button to Refresh the UI. But I wasn't updating my UI quite the way you are.
If you still have the need to do both, here is, roughly, how you can flow your app...
Create an
InitailizeBackgroundWorker() method
along with the InitializeTimer so you have
it already initalized before the
Timer fires.
Then set the Timer.Tick
to call the
BackgroundWorker.RunWorkerAsync()
Then you can do all the UI updates from within the RunWorkerAsync by
using the
BackgroundWorker.ReportProgress().