I have a BackgroundWorker that creates a timer. The timer makes repeated calls to a DataTable. I only want the BackgroundWorker.RunWorkerCompleted event to get called when the timer stops. How do I do this?
Thanks.
Just create a loop in the BackgroundWorker's DoWork event handler and repeat the loop until the timer stops. More or less like so:
var worker = new BackgroundWorker();
worker.DoWork += (sender, e) =>
{
var timer = new Timer();
timer.Elapsed += (s, _e) =>
{
// call the database
};
timer.Start();
while (timer.Enabled)
{
// at some point: timer.Stop();
}
// if we are here, timer is no longer Enabled
// RunWorkerCompleted event will be fired next
};
(Obviously I ommitted setting the timer's Interval etc.)
BackGroundWorker.RunWorkerCompleted is called after your DoWork event completed. So if you ensure the DoWork event completes your RunWorkerCompleted event should be called by the background worker.
Another solution is to create the timer outside your background worker and control the background worker instance from the timer. In the timer event, check if the IsBusy property is set to false and start the background worker or skip if IsBusy is true.
Sample:
_worker.DoWork += (sender, e) =>
{
BackgroundWorker worker = sender as BackgroundWorker;
// Do your database stuff
e.Result = databaseResult;
}
_timer.Elapsed += (source, e) =>
{
if(!_worker.IsBusy)
{
_worker.RunWorkerAsync();
}
}
Related
I have a System.Timers.Timer instance created in the main thread. Now I call timer.Stop() to try to terminate that time and want to wait until the timer is really terminated. How could I do that?
Is there any similar method like System.Threading.Thread.Join()?
Here are some codes
//the main thread:
var aTimer = New Timer();
aTimer.Elapsed += SomeTimerTask;
aTimer.AutoReset = True;
aTimer.Start();
//some other logic...
//stop that timer:
aTimer.Stop();
//now I need to wait until that timer is really stopped,
//but I cannot touch the method SomeTimerTask().
//so I need something like System.Threading.Thread.Join()...
You could make use of a ResetEvents which are wait handles which can block a thread until you set the state to signaled:
class TimerAndWait
{
private ManualResetEvent resetEvent = new ManualResetEvent(false);
public void DoWork()
{
var aTimer = new System.Timers.Timer(5000);
aTimer.Elapsed += SomeTimerTask;
aTimer.Elapsed += ATimer_Elapsed;
aTimer.AutoReset = true;
aTimer.Start();
// Do something else
resetEvent.WaitOne(); // This blocks the thread until resetEvent is set
resetEvent.Close();
aTimer.Stop();
}
private void ATimer_Elapsed(object sender, ElapsedEventArgs e)
{
resetEvent.Set();
}
}
If you want a async/task-based solution you have to use the ThreadPool.RegisterWaitForSingleObject method
The Timer does not fire up the Elapsed when you call stop as you can read in the docs of the Stop()-Method:
Stops raising the Elapsed event by setting Enabled to false.
The Elapsed-Event is only triggered when the Timers Enabled-Property is set to true and the given Interval (which you have to set) is elapsed (this can happen multiple times).
So if you stop your Timer before the Interval is elapsed, you might have to trigger your code in some other way.
currently facing this issue with a timer. I basically want to create a timer which will execute after a button press. this will then count to 5 and then close the window which is created from the class. Below is what I have at the moment.
public void startMessageIndicator(string message, bool completed)
{
messageIndicator.Text = "completed";
window.Show();
aTimer = new System.Timers.Timer(5000);
aTimer.Enabled = true;
aTimer.Start();
aTimer.Elapsed += new ElapsedEventHandler(timerElapsed);
aTimer.AutoReset = true;
}
public void timerElapsed(object sender, ElapsedEventArgs e)
{
window.Close();
st.Clear();
aTimer.Enabled = false;
}
When I compile the code I face no issues however when I go into the debugger and use breakpoints it does not seem to run window.close() and just gets stuck on that line.
Any ideas what I am doing wrong
You should call a dispatcher on the window itself to update UI thread.
Replace
window.close();
with
window.Invoke((MethodInvoker)delegate
{
window.Close();
});
You can use this
ExecuteSecure(window.Close);
//OR
ExecuteSecure(() => window.Close());
//---
private void ExecuteSecure(Action action)
{
if (InvokeRequired)
{
Invoke(new MethodInvoker(() => action()));
}
else
{
action();
}
}
Don't forget that method in Timer's Tick event handler is executed in a separate thread while field window was created in the UI thread. Attempt to invoke method from other thread than the thread were it was created leads to InvalidOperationException... So you can simply change your code from:
window.Close();
To :
this.Invoke(new Action(() => fr.Close()), null);
Now you invoke action on a UI thread and it should work as expected.
I am using a timer as
System.Threading.Timer clipboardTimer = new Timer(ClearClipboard);
Next, I change its interval as
clipboardTimer.Change(1000, 30000);
In the handle timeout function, i.e. ClearClipboard, I want to clear the clipboard as
void ClearClipboard(object o)
{
Clipboard.SetText("");
}
but there is System.Unauthorised exception. Maybe, this is because there are two different threads. So, how can I invoke clear clipboard efficiently?
This error occurs because the Timer event fires on a separate thread than the UI thread. You can change a UI element in one of two ways. The first is to tell the Dispatcher object to execute the code on the UI thread. If your object with the Timer is a DependencyObject (e.g. PhoneApplicationPage), you can use the Dispatcher property. This is done with the BeginInvoke method.
void ClearClipboard(object o)
{
Dispatcher.BeginInvoke(() => Clipboard.SetText(""));
}
If your object is not a DependencyObject, you can use the Deployment object to access the Dispatcher.
void ClearClipboard(object o)
{
Deployment.Current.Dispatcher.BeginInvoke(() => Clipboard.SetText(""));
}
The second option is to use the DispatcherTimer instead of the Timer. The DispatcherTimer event does fire on the UI Thread!
// Create the timer
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(3);
timer.Tick += TimerOnTick;
// The subscription method
private void TimerOnTick(object sender, EventArgs eventArgs)
{
Clipboard.SetText("");
}
Ask Dispatcher to run Clipboard.SetText(""); on UI thread, as timer's timeout event is raised on a non-UI thread and you cannot alter controls created by the UI thread from another thread
Try something like this
void ClearClipboard(object o)
{
Dispatcher.Invoke( () => { Clipboard.SetText(""); });
}
You'll need to Invoke the method on the GUI thread. You can do that by calling Control.Invoke:
control.Invoke(new Action(() => control.Text = "new text")));
I have a BackgroundWorker _worker
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
_timer = new System.Timers.Timer();
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
_timer.Interval = 5000;
_timer.Start();
}
When it gets to the line _timer.Start() it thinks it has finished so fires the RunWorkerCompleted event.
I don't want it to finish until the _timer.Interval time has been reached and the _timer Elapsed event has been trigger:
void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
for (int i = 1; i < 20; i++)
{
if (listBox1.InvokeRequired)
listBox1.Invoke((Action)(() => listBox1.Items.Add("Do Things Thread")));
else
listBox1.Items.Add("Do Things Completed");
_worker.ReportProgress((int)(((decimal)i / (decimal)20) * 100));
Thread.Sleep(1000);
}
_timer.Stop();
}
Because I need the BackgroundWorker to report back some progress.
How do I do this. I need it to run on a different thread.
So to round up the discussion and the solution.
System.Timers.Timer will automatically thread the timer event for you unless you supply a sync object, as discussed in MSDN:
If the SynchronizingObject property is null, the Elapsed event is
raised on a ThreadPool thread. If processing of the Elapsed event
lasts longer than Interval, the event might be raised again on another
ThreadPool thread. In this situation, the event handler should be
reentrant.
This means that the background worker becomes superfluos. You can simply have your timer event code run as is (as it uses invoke to interact with the UI anyhow).
It does mean that these events can run concurrently if they take a long time. However, you can of course stop and start the timer in the event callback.
Your _timer_Elapsed event is on a different thread. The instance of timer will expire as soon as the control flow passes through the _worker_DoWork function. The scope of your timer object variable is restricted to the function and hence it will not work this way.
I would suggest that you put put Thread.sleep(5000) in the timer _worker_dowork function itself. It will not affect your application as the thread will sleep and the gui will still be responsive.
I think, just by playing around with what I've done, is that I've invoked the progress bar, so I don't need the ReportProgress:
_worker.ReportProgress((int)(((decimal)i / (decimal)20) * 100));
Changed to :
if (progressBar1.InvokeRequired)
progressBar1.Invoke((Action)(() => progressBar1.Value = (int)(((decimal)i / (decimal)20) * 100)));
else
progressBar1.Value = (int)(((decimal)i / (decimal)20) * 100);
That means, I don't have to suspend the BackgroundWorker, and the timer was invoked on a different thread and will remain until disposed of????
I have c# app that has UI and background threads. Based on user input I like to stop and start the background thread. I have two options here as I see:
1) totally stop and then start background thread as new thread ( I have not been able to this. I keep getting my process ended message)
2) Pause the background thread until user click run again.
Here is the code that I call again after bw.CancelAsync();
private void StartBackgroundWorker()
{
bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync("Background Worker");
}
you can't start and stop a background worker like that, but in your DoWork event, you can have it ask whether it should execute or wait.
you can also subclass BackgroundWorker (override the OnDoWork() method), and add start/pause methods to it that toggle a private wait handle, which is much nicer than having your UI know about the ManualResetEvent.
//using System.Threading;
//the worker will ask this if it can run
ManualResetEvent wh = new ManualResetEvent(false);
//this holds UI state for the start/stop button
bool canRun = false;
private void StartBackgroundWorker()
{
bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync("Background Worker");
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
//it waits here until someone calls Set() on wh (via user input)
// it will pass every time after that after Set is called until Reset() is called
wh.WaitOne()
//do your work
}
}
//background worker can't start until Set() is called on wh
void btnStartStop_Clicked(object sender, EventArgs e)
{
//toggle the wait handle based on state
if(canRun)
{
wh.Reset();
}
else {wh.Set();}
canRun= !canRun;
//btnStartStop.Text = canRun ? "Stop" : "Start";
}
You can always abort a thread and catch the ThreadAbortedException. Im not sure if this is the most neat solution since an exception causes a lot of overhead but i think it is better than spreading WaitOne in the code like Dan suggested.
Another solution is to inherit from the thread class, and add a function to this class that stops or pauses the thread. This way you can hide the details of the implementation.