What is the best solution to quickly cancel long running processes inside background worker?
For example, we have such situation:
private void DoWork(object sender, DoWorkEventArgs e)
{
...
for (int i = 0; i < items; i++)
{
if (_worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
VeryLongRunningProcess();
}
}
}
private void VeryLongRunningProcess()
{
var a = Test();
var b = Test2();
Thread.Sleep(5000);
var c = Test3();
}
In such case, VeryLongRunningProcess() will be not finished on pressing cancel until he finished everything inside his body.
What to do in such cases?
I tried to pass (BackgroundWorker)sender to VeryLongRunningProcess() as param and inside this method check for CancellationPending, but i dont know is this correct way or not
If the problem is isolated your VeryLongRunningProcess from classes like the worker, you can use a Func as a parameter and leave outside your method the worker access
private void VeryLongRunningProcess(Func<bool> isCancelled)
{
var a = Test();
if (isCancelled())
{
return;
}
var b = Test2();
if (isCancelled())
{
return;
}
Thread.Sleep(5000);
var c = Test3();
}
Inside your method, you may check if you must cancel the operation as many times you need. And you can use the Func as a parameter in other methods like Test1, Test2... if any of them takes long time to finish.
Then, you invoke your method in this form:
VeryLongRunningProcess(() => _worker.CancellationPending);
As other people comment, maybe interesting use async/await.
UPDATE
Another way to do if you want choose the use or not of the cancellation:
private void VeryLongRunningProcess(Func<bool> isCancelled = null)
{
var a = Test();
// Or: isCancelled != null && isCancelled()
if (isCancelled?.Invoke() ?? false)
{
return;
}
// ...
}
Normally you should create long-running process as "async" method (public async Task or Task DoWork()) for resources destribution purposes. "CancelationToken" enables cooperative cancellation between threads, thread pool work items. Also it is possible to propagate a callback delegate that can be invoked when Cancellation Token cancelled or function is compleete.
Related
I have a C# Windows Service that runs a few tasks inside.
One of the tasks is a infinite async looping and the others are triggered from a Timer and then execute the task.
private readonly QueueProcessor _queueProcessor;
protected override void OnStart(string[] args)
{
// first task
_queueTask = _queueProcessor.Run(_cancellation.Token);
// second task
affiliate_timer = new System.Timers.Timer();
affiliate_timer.AutoReset = true;
affiliate_timer.Interval = _model.Interval_Affiliate * 60000;
affiliate_timer.Elapsed += new
System.Timers.ElapsedEventHandler(affiliate_timer_Elapsed);
// third task
invoice_timer = new System.Timers.Timer();
invoice_timer.AutoReset = true;
invoice_timer.Interval = _model.Interval_Invoice * 60000;
invoice_timer.Elapsed += new
System.Timers.ElapsedEventHandler(invoice_timer_Elapsed);
}
private void invoice_timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
{
if (!_isAffiliateBusy)
{
_isAffiliateBusy= true;
var task = Task.Run(() => StartAffiliateTask());
task.Wait();
_isAffiliateBusy= false;
}
}
private void invoice_timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
{
if (!_isInvoiceBusy)
{
_isInvoiceBusy = true;
var task = Task.Run(() => StartInvoiceTask());
task.Wait();
_isInvoiceBusy = false;
}
}
private void StartAffiliateTask()
{
_affiliateModule = new Modules.Affiliate();
_affiliateModule.RunSync();
}
private void StartInvoiceTask()
{
_invoiceModule = new Modules.Invoice();
_invoiceModule.RunSync();
}
This is my QueueProcessor class that implements await/async to execute a infinite looping job:
public class QueueProcessor
{
private readonly IQueueBroker _serviceBroker;
public QueueProcessor()
{
}
public async Task Run(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var receiveMessageResponse = await _serviceBroker.ReceiveMessageAsync("test", cancellationToken);
if (!receiveMessageResponse.Messages.Any())
{
continue;
}
foreach (var message in receiveMessageResponse.Messages)
{
// some other tasks here...
await _serviceBroker.DeleteMessageAsync(message, cancellationToken);
}
}
}
}
My Affiliate and Invoice module classes doesn't implement any await/async code inside looks like this:
public class Affiliate
{
/// <summary>
/// Start the sync process
/// </summary>
public void RunSync()
{
try
{
// some code here...
}
catch (Exception ex)
{
}
}
}
My question is:
When my queue procesor infinite loop is running, does my other tasks that are triggered by the timers still can run independently?
When I use:
var task = Task.Run(() => StartAffiliateTask());
task.Wait();
Does the Wait method stop the whole service thread until this task is finished? or that won't block my StartInvoiceTask to run independantly?
Any recommendation on the best way to have my 3 tasks running independant on each other?
Summing up multiple potential issues:
Race condition (access/write to _isBusy).
Potential deadlock (in low ThreadPool size).
Potential incosistent state of flag in case of errors or thread aborts (_isBusy can be left in 'true' state).
Further I will assume your 'task' should be running in single instance, so we will disgard timer callbacks if it is still running.
You should change your timer event handlers like so (best to just wrap it in some kind of class):
//the flag, do mention volatile modifier - it tells particular
//systems to watch for variable changes by reference,
//instead of just copying it into thread stack by value.
private volatile bool _isAffiliateBusy = false;
//sync object for flag to eliminate race condition
private object _affiliateSync = new object();
private void affiliate_timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
{
//very fast lookup at flag to filter threads which comes when task is still processing
if(_isAffiliateBusy)
return;
lock(_affiliateSync) //taking lock
{
//checking again for those threads which 'happen' to be faster than you think.
if(_isAffiliateBusy)
return;
//aquire lock for business 'task'
_isAffiliateBusy = true;
}
try
{
StartAffiliateTask();
}
finally
{
//resetting singleton business 'task' lock.
//do not forget to use finally block, to handle disposing
//even if something rise up in 'try' section - you will not be left with invalid state of flag.
_isAffiliateBusy = false;
}
}
As far as I can see AutoResetEvent and ManualEvent simply control a single signal for cross-thread communication.
Often you would want to know some sort of result in the waiting thread and I can only see the option of storing a class member which is accessed by each thread e.g.:
this.WaitHandle = new AutoResetEvent(false);
DoStuff();
WaitHandle.WaitOne();
lock(this._lock)
{
if(this.Result ...){...}
}
void DoStuff()
{
...
lock(this._lock)
{
this.Result = ...;
}
this.WaitHandle.Set();
}
This is a bit cumbersome so I wondered if there is something built in that will let me pass a value when signalling?
The best approach would be to use the async patterns consistently in all your code.
If you want to actually block execution and wait for the result to be available in a blocking way using traditional methods, you can use a BlockingCollection<T> as a channel to both communicate the result of the operation and that the operation is finished at the same time.
private void button1_Click(object sender, EventArgs e)
{
BlockingCollection<string> blockingQueue = new BlockingCollection<string>();
// Start DoStuff on parallel thread
Task.Run(() => DoStuff(blockingQueue));
// Wait for DoRun to finish AND get the result at the same time
string result = blockingQueue.Take();
MessageBox.Show(result);
}
private void DoStuff(BlockingCollection<string> result)
{
// Simulate work
Thread.Sleep(1000);
// return result
result.Add("SomeResultValue");
}
This also allows you to have two threads adding stuff to the queue and retrieving stuff from the queue at the same time in parallel, the consumer always blocking as long as there is no new data available.
private void button1_Click(object sender, EventArgs e)
{
BlockingCollection<string> blockingQueue = new BlockingCollection<string>();
// Start DoStuff on parallel thread
Task.Run(() => DoStuff(blockingQueue));
// Wait for something to be returned from DoStuff and handle it
foreach (string data in blockingQueue.GetConsumingEnumerable())
{
textBox1.AppendText(data + Environment.NewLine);
}
MessageBox.Show("Finished");
}
private void DoStuff(BlockingCollection<string> result)
{
for (int i = 1; i <= 10; i++)
{
// Simulate work
Thread.Sleep(1000);
// return result
result.Add("Result number " + i);
}
// Signal we are done
result.CompleteAdding();
}
If you use a BlockingCollection with BoundedCapacityset to 1, trying to add to the collection would actually wait until the previous value has been removed.
Here's my situation:
I have a WPF application, where I have a method which takes a lot of time to be completed. I don't want to lose UI responsiveness, so I'd like to call that method in another thread.
I won't paste here my entire code, because it's too long, instead I wrote this short program, which represents well what I'm dealing with:
public void MainWindow()
{
InitializeComponent();
ProcessThread = new Thread(TimeConsumingMethod);
ProcessThread.Name = "ProcessThread";
ProcessThread.Start();
}
public void TimeConsumingMethod()
{
this.Dispatcher.Invoke(() =>
{
MytextBlock.Text = "new text";
MyOtherTextBlock.Text = "Hello";
});
for (int i = 0; i < 50; i++)
{
Debug.WriteLine("Debug line " + i);
}
if (MyRadioButton.IsChecked == false) //????????????????
{
while (true)
{
if (DateTime.Now >= timePicker.Value)
break;
}
}
OtherMethod();
}
Actually, I have two questions for the above code:
1. Everytime I want to access UI controls in my code I have to use this.Dispatcher.Invoke() =>.... Is it the right thing to do? I mean, I have a few places in my method (in my real code) where I check the state of some controls and everytime I need to do his Dispatcher.invoke thing - isn't there a better way to acces these controls?
2. In the code above, there's IF block in the end - in that block I'm checking the state of my RadioButton. Inside of that IF, I have a time consuming code. I cannot just do this:
this.Dispatcher.Invoke(() =>
{
if (MyRadioButton.IsChecked == false) //????????????????
{
while (true)
{
if (DateTime.Now >= timePicker.Value)
break;
}
}
});
That code would tell my UI thread to handle this if block - but I don't want that! That would cause the whole UI to freeze until this IF block gets done. How should I handle this situation?
Well, there are a lot of ways to implement what you are trying to do. One of them might look like this:
public MainWindow() {
InitializeComponent();
Initialize(); //do some intialization
}
private async void Timer_Tick(object sender, EventArgs e) {
if (DateTime.Now >= timePicker.SelectedDate) { //check your condition
timer.Stop(); //probably you need to run it just once
await Task.Run(() => OtherMethod()); //instead of creating thread manually use Thread from ThreadPool
//use async method to avoid blocking UI during long method is running
}
}
private readonly DispatcherTimer timer = new DispatcherTimer(); //create a dispatcher timer that will execute code on UI thread
public void Initialize() {
MytextBlock.Text = "new text";
MyOtherTextBlock.Text = "Hello"; //access UI elements normally
for (var i = 0; i < 50; i++) {
Debug.WriteLine("Debug line " + i);
}
if (MyRadioButton.IsChecked == false)
{
timer.Interval = TimeSpan.FromSeconds(10); // during init setup timer instead of while loop
timer.IsEnabled = true;
timer.Tick += Timer_Tick; //when 10 sec pass, this method is called
timer.Start();
}
}
public void OtherMethod() {
//long running method
Thread.Sleep(1000);
}
I've added some comments, but the main idea is this:
Don't create threads manually, use ThreadPool
Don't loop to wait for something, use timer to periodically check for it
Use async method when you have I/O Tasks
I think I'm missing something obvious here, but how do I update the GUI when using a task and retrieving the value? (I'm trying to use await/async instead of BackgroundWorker)
On my control the user has clicked a button that will do something that takes time. I want to alert the parent form so it can show some progress:
private void ButtonClicked()
{
var task = Task<bool>.Factory.StartNew(() =>
{
WorkStarted(this, new EventArgs());
Thread.Sleep(5000);
WorkComplete(this, null);
return true;
});
if (task.Result) MessageBox.Show("Success!");//this line causes app to block
}
In my parent form I'm listening to WorkStarted and WorkComplete to update the status bar:
myControl.WorkStarting += (o, args) =>
{
Invoke((MethodInvoker) delegate
{
toolStripProgressBar1.Visible = true;
toolStripStatusLabel1.Text = "Busy";
});
};
Correct me if I'm wrong, but the app is hanging because "Invoke" is waiting for the GUI thread to become available which it won't until my "ButtonClicked()" call is complete. So we have a deadlock.
What's the correct way to approach this?
You're blocking the UI thread Task.Result blocks until the task is completed.
Try this.
private async void ButtonClicked()
{
var task = Task<bool>.Factory.StartNew(() =>
{
WorkStarted(this, new EventArgs());
Thread.Sleep(5000);
WorkComplete(this, null);
return true;
});
await task;//Wait Asynchronously
if (task.Result) MessageBox.Show("Success!");//this line causes app to block
}
You can use Task.Run to execute code on a background thread. The Task-based Asynchronous Pattern specifies a pattern for progress updates, which looks like this:
private async void ButtonClicked()
{
var progress = new Progress<int>(update =>
{
// Apply "update" to the UI
});
var result = await Task.Run(() => DoWork(progress));
if (result) MessageBox.Show("Success!");
}
private static bool DoWork(IProgress<int> progress)
{
for (int i = 0; i != 5; ++i)
{
if (progress != null)
progress.Report(i);
Thread.Sleep(1000);
}
return true;
}
If you are targeting .NET 4.0, then you can use Microsoft.Bcl.Async; in that case, you would have to use TaskEx.Run instead of Task.Run. I explain on my blog why you shouldn't use Task.Factory.StartNew.
I have a form with 2 comboboxes on it. And I want to fill combobox2.DataSource based on combobox1.Text and combobox2.Text (I assume that the user has completed input in combobox1 and is in the middle of inputting in combobox2). So I have an event handler for combobox2 like this:
private void combobox2_TextChanged(object sender, EventArgs e)
{
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
As far as building DataSource is time-consuming process (it creates a request to database and executes it) I decided that it's better to perform it in another process using BackgroundWorker. So there's a scenario when cmbDataSourceExtractor hasn't completed its work and the user types one more symbol. In this case I get an exception on this line
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues ); about that BackgroundWorker is busy and cannot perform several actions in the same time.
How to get rid of this exception?
CancelAsync doesn't actually abort your thread or anything like that. It sends a message to the worker thread that work should be cancelled via BackgroundWorker.CancellationPending. Your DoWork delegate that is being run in the background must periodically check this property and handle the cancellation itself.
The tricky part is that your DoWork delegate is probably blocking, meaning that the work you do on your DataSource must complete before you can do anything else (like check for CancellationPending). You may need to move your actual work to yet another async delegate (or maybe better yet, submit the work to the ThreadPool), and have your main worker thread poll until this inner worker thread triggers a wait state, OR it detects CancellationPending.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx
http://www.codeproject.com/KB/cpp/BackgroundWorker_Threads.aspx
If you add a loop between the CancelAsync() and the RunWorkerAsync() like so it will solve your problem
private void combobox2_TextChanged(object sender, EventArgs e)
{
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
while(cmbDataSourceExtractor.IsBusy)
Application.DoEvents();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
The while loop with the call to Application.DoEvents() will hault the execution of your new worker thread until the current one has properly cancelled, keep in mind you still need to handle the cancellation of your worker thread. With something like:
private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
{
if (this.cmbDataSourceExtractor.CancellationPending)
{
e.Cancel = true;
return;
}
// do stuff...
}
The Application.DoEvents() in the first code snippet will continue to process your GUI threads message queue so the even to cancel and update the cmbDataSourceExtractor.IsBusy property will still be processed (if you simply added a continue instead of Application.DoEvents() the loop would lock the GUI thread into a busy state and would not process the event to update the cmbDataSourceExtractor.IsBusy)
You will have to use a flag shared between the main thread and the BackgroundWorker, such as BackgroundWorker.CancellationPending. When you want the BackgroundWorker to exit, just set the flag using BackgroundWorker.CancelAsync().
MSDN has a sample: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancellationpending.aspx
MY example . DoWork is below:
DoLengthyWork();
//this is never executed
if(bgWorker.CancellationPending)
{
MessageBox.Show("Up to here? ...");
e.Cancel = true;
}
inside DoLenghtyWork :
public void DoLenghtyWork()
{
OtherStuff();
for(int i=0 ; i<10000000; i++)
{ int j = i/3; }
}
inside OtherStuff() :
public void OtherStuff()
{
for(int i=0 ; i<10000000; i++)
{ int j = i/3; }
}
What you want to do is modify both DoLenghtyWork and OtherStuff() so that they become:
public void DoLenghtyWork()
{
if(!bgWorker.CancellationPending)
{
OtherStuff();
for(int i=0 ; i<10000000; i++)
{
int j = i/3;
}
}
}
public void OtherStuff()
{
if(!bgWorker.CancellationPending)
{
for(int i=0 ; i<10000000; i++)
{
int j = i/3;
}
}
}
The problem is caused by the fact that cmbDataSourceExtractor.CancelAsync() is an asynchronous method, the Cancel operation has not yet completed when cmdDataSourceExtractor.RunWorkerAsync(...) exitst. You should wait for cmdDataSourceExtractor to complete before calling RunWorkerAsync again. How to do this is explained in this SO question.
My answer is a bit different because I've tried these methods but they didn't work. My code uses an extra class that checks for a Boolean flag in a public static class as the database values are read or where I prefer it just before an object is added to a List object or something as such. See the change in the code below. I added the ThreadWatcher.StopThread property. for this explation I'm nog going to reinstate the current thread because it's not your issue but that's as easy as setting the property to false before accessing the next thread...
private void combobox2_TextChanged(object sender, EventArgs e)
{
//Stop the thread here with this
ThreadWatcher.StopThread = true;//the rest of this thread will run normally after the database function has stopped.
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
while(cmbDataSourceExtractor.IsBusy)
Application.DoEvents();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
all fine
private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
{
if (this.cmbDataSourceExtractor.CancellationPending)
{
e.Cancel = true;
return;
}
// do stuff...
}
Now add the following class
public static class ThreadWatcher
{
public static bool StopThread { get; set; }
}
and in your class where you read the database
List<SomeObject>list = new List<SomeObject>();
...
if (!reader.IsDbNull(0))
something = reader.getString(0);
someobject = new someobject(something);
if (ThreadWatcher.StopThread == true)
break;
list.Add(something);
...
don't forget to use a finally block to properly close your database connection etc. Hope this helps! Please mark me up if you find it helpful.
In my case, I had to pool database for payment confirmation to come in and then update WPF UI.
Mechanism that spins up all the processes:
public void Execute(object parameter)
{
try
{
var url = string.Format("{0}New?transactionReference={1}", Settings.Default.PaymentUrlWebsite, "transactionRef");
Process.Start(new ProcessStartInfo(url));
ViewModel.UpdateUiWhenDoneWithPayment = new BackgroundWorker {WorkerSupportsCancellation = true};
ViewModel.UpdateUiWhenDoneWithPayment.DoWork += ViewModel.updateUiWhenDoneWithPayment_DoWork;
ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerCompleted += ViewModel.updateUiWhenDoneWithPayment_RunWorkerCompleted;
ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerAsync();
}
catch (Exception e)
{
ViewModel.Log.Error("Failed to navigate to payments", e);
MessageBox.Show("Failed to navigate to payments");
}
}
Mechanism that does checking for completion:
private void updateUiWhenDoneWithPayment_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(30000);
while (string.IsNullOrEmpty(GetAuthToken()) && !((BackgroundWorker)sender).CancellationPending)
{
Thread.Sleep(5000);
}
//Plug in pooling mechanism
this.AuthCode = GetAuthToken();
}
Mechanism that cancels if window gets closed:
private void PaymentView_OnUnloaded(object sender, RoutedEventArgs e)
{
var context = DataContext as PaymentViewModel;
if (context.UpdateUiWhenDoneWithPayment != null && context.UpdateUiWhenDoneWithPayment.WorkerSupportsCancellation && context.UpdateUiWhenDoneWithPayment.IsBusy)
context.UpdateUiWhenDoneWithPayment.CancelAsync();
}
I agree with guys. But sometimes you have to add more things.
IE
1) Add this worker.WorkerSupportsCancellation = true;
2) Add to you class some method to do the following things
public void KillMe()
{
worker.CancelAsync();
worker.Dispose();
worker = null;
GC.Collect();
}
So before close your application your have to call this method.
3) Probably you can Dispose, null all variables and timers which are inside of the BackgroundWorker.