Thread pass data and window close - c#

I have created a simple WPF project where on button click I create a separate thread with new window, and pass data to it. On application Exit I am trying to close safely that thread/window. However, I get occasionally the bellow exception, which causes application instabilities.
So my question is how to handle that situation gracefully. Thx
On line:
Dispatcher.Invoke(new Action(() =>
I have
A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll
Additional information: Thread was being aborted.
My View has the following code:
Constructor
public MyView(ConcurrentQueue<MyItem> actionReports, ManualResetEvent actionCompletedEvent, string actionName)
{
_actionReports = actionReports;
_actionCompletedEvent = actionCompletedEvent;
_actionName = actionName;
InitializeComponent();
DataContext = this;
this.Loaded += MyView_Loaded;
}
void MyView_Loaded(object sender, RoutedEventArgs e)
{
var worker = new BackgroundWorker();
worker.DoWork += (o, ea) =>
{
while (true)
{
if (_actionCompletedEvent.WaitOne(0))
{
// Issue
Dispatcher.Invoke(new Action(() =>
{
Close();
}));
Thread.Sleep(100);
}
while (!_actionReports.IsEmpty)
{
// Do some stuff
}
}
};
worker.RunWorkerAsync();
}
Initialize of Window
public WindowLauncher(ManualResetEvent actionCompletedEvent, ManualResetEvent reportWindowClosedEvent, string actionName)
{
_actionCompletedEvent = actionCompletedEvent;
_reportWindowClosedEvent = reportWindowClosedEvent;
_actionName = actionName;
Thread thread = new Thread(new ThreadStart(() =>
{
_reportWindow = new MyView(_messageQueue, _actionCompletedEvent, actionName);
_reportWindow.Show();
// InvokeShutdown to terminate the thread properly
_reportWindow.Closed += (sender, args) =>
{
_reportWindow.Dispatcher.InvokeShutdown();
};
_resetEvent.Set();
Dispatcher.Run();
}));
thread.Name = "MyWindowThread";
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
}

I'd normally at least try to cancel the BackgroundWorker async action in the Window.OnClosing event and catch the pending cancellation. You'll still need to watch out for ThreadAbortExceptions but only if your async process is long-running.
private BackgroundWorker _worker;
private void MyView_Loaded(object sender, RoutedEventArgs e)
{
_worker = new BackgroundWorker();
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += (o, ea) =>
{
while (true)
{
if (_actionCompletedEvent.WaitOne(0))
{
if (_worker.CancellationPending)
{
ea.Cancel = true;
return;
}
// Issue
Dispatcher.Invoke(new Action(() =>
{
Close();
}));
Thread.Sleep(100);
}
while (!_actionReports.IsEmpty)
{
// Do some stuff
}
}
};
}
protected override void OnClosing(CancelEventArgs e)
{
_worker.CancelAsync();
}

Background threads are aborted automatically by the runtime when your app exits. Normally they do not throw ThreadAbortExceptions (see Foreground and Background threads)
Invoking the dispatcher causes a method to be run on the dispatch thread, but since you used Invoke() rather than BeginInvoke(), the dispatcher needs to inform the background thread that the method finished. I suspect this is where the exception is being raised, but this is only conjecture on my part.
Try catching the exception like this:
try
{
Dispatcher.Invoke(new Action(() =>
{
Close();
}));
}
catch (ThreadAbortException)
{
Thread.ResetAbort();
}
Although this may not help if the exception is being raised on the dispatch thread. If this doesn't work then try BeginInvoke() instead.
BTW, you can probably reproduce this bug more easily if you stick some kind of delay in the start of your Invoked lambda and close the app at that point. That means the background thread will be aborted by the time the lambda completes.

Related

Updating status strip label async/await

I have a WinForm, with a toolStripStatusLabel. There is a button, which spawns a new thread to perform its task. The status label needs to update during, and after this task is completed. The GUI elements are in the main thread. If I want to achieve this, can I place the relevant lines to update the label where the comments are below in the code snippet below? Also, I need to have another form open when this label is clicked. From my understanding of asynchronous coding, this should be straightforward, involving an event handler for the label, and the fact that control will return to the caller of the async method. Is this correct? I am relatively new to multithreaded and asynchronous programming, so I am quite confused.
// this is running in the main GUI thread
private async void Export_execute_Click(object sender, System.EventArgs args)
{
try
{
await System.Threading.Tasks.Task.Run(() => do_export(filename, classes, System.TimeZoneInfo.ConvertTimeToUtc(timestamp)));
// if this is successful, status label should be update (task successful)
}
catch (System.Exception e)
{
// status label should be updated (task failed)
}
}
If there is something literally awaitable in the Export method then I think to make it an async method would be better.
private async void Export_execute_Click(object sender, EventArgs e)
{
try
{
await ExportAsync("file1", "classA", DateTime.Now);
toolStripStatusLabel.Text = $"Export finished at {DateTime.Now}";
}
catch (Exception ex)
{
toolStripStatusLabel.Text = $"Export failed, {ex.ToString()}";
}
}
private async Task ExportAsync(string fileName, string classes, DateTime timestamp)
{
toolStripStatusLabel.Text = $"Export start at {timestamp}";
await Task.Delay(TimeSpan.FromSeconds(5));
toolStripStatusLabel.Text = $"Have first half done {timestamp}";
await Task.Delay(TimeSpan.FromSeconds(5));
}
private void toolStripStatusLabel_Click(object sender, EventArgs e)
{
Form2 frm2 = new Form2();
frm2.Show();
}
The standard way to report progress is to use the IProgress<T> interface. There is already an implementation of this interface that you can use (Progress<T>), and is generic so that you can supply any type of argument you want. In the example bellow the argument is a string. The key point is that the event Progress.ProgressChanged is running in the UI thread, so you don't have to worry about it.
// This will run in the UI thread
private async void Export_Execute_Click(object sender, EventArgs args)
{
try
{
var progress = new Progress<string>();
progress.ProgressChanged += ExportProgress_ProgressChanged;
// Task.Factory.StartNew allows to set advanced options
await Task.Factory.StartNew(() => Do_Export(filename, classes,
TimeZoneInfo.ConvertTimeToUtc(timestamp), progress),
CancellationToken.None, TaskCreationOptions.LongRunning,
TaskScheduler.Default);
toolStripStatusLabel.Text = $"Export completed successfully";
}
catch (Exception e)
{
toolStripStatusLabel.Text = $"Export failed: {e.Message}";
}
}
// This will run in the UI thread
private void ExportProgress_ProgressChanged(object sender, string e)
{
toolStripStatusLabel.Text = e;
}
// This will run in a dedicated background thread
private void Do_Export(string filename, string classes, DateTime timestamp,
IProgress<string> progress)
{
for (int i = 0; i < 100; i += 10)
{
progress?.Report($"Export {i}% percent done");
Thread.Sleep(1000);
}
}
How about a BackgroundWorker instead of your current Task? I prefer these because they allow easy communication between the main thread and the worker.
Note that Export_execute_Click is no longer marked as async in this scenario.
Example:
private void Export_execute_Click(object sender, System.EventArgs args) {
// Method level objects are accessible throughout this process
bool error = false;
// Process
BackgroundWorker worker = new BackgroundWorker {
WorkerReportsProgress = true
};
// This executes on main thread when a progress is reported
worker.ProgressChanged += (e, ea) => {
if (ea.UserState != null) {
// ea.UserState.ToString() contains the string progress message
}
};
// This executes as an async method on a background thread
worker.DoWork += (o, ea) => {
try {
var response = do_export(filename, classes, System.TimeZoneInfo.ConvertTimeToUtc(timestamp)));
if (response == whatever) {
worker.ReportProgress(0, "Response from do_export() was `whatever`");
} else {
worker.ReportProgress(0, "Response from do_export() was something bad");
error = true;
}
} catch (System.Exception e) {
worker.ReportProgress(0, $"do_export() failed: {e}");
}
};
// This executes on the main thread once the background worker has finished
worker.RunWorkerCompleted += async (o, ea) => {
// You can communicate with your UI normally again here
if (error) {
// You had an error -- the exception in DoWork() fired
} else {
// You're all set
}
// If you have a busy-indicator, here is the place to disable it
// ...
};
// I like to set a busy-indicator here, some sort of ajax-spinner type overlay in the main UI, indicating that the process is happening
// ...
// This executes the background worker, as outlined above
worker.RunWorkerAsync();
}

How can I synchronously access/call an object from multiple background workers

I have an object on my main UI thread which sends serial commands to an arduino and I need to be able to access this object and have it send serial commands to the arduino in the order they are received from within two separate background workers.
I have searched around and found many methods to update a UI from multiple backgroundworkers but the problem is most of the solutions are geared at objects that were designed with multi threading in mind. I.e. you can do "GUIobject.Invoke(etc)".
var driver = Periphrials.InitializeArduinoDriver();
StillMonitor = new BackgroundWorker();
StillMonitor.WorkerSupportsCancellation = true;
StillMonitor.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (StillMonitor.CancellationPending)
{ break; }
(driver.Send(new DigitalReadRequest(properties.StillLowSwitch)).PinValue.ToString() == "Low")
} while (true);
});
The "driver.send......" part is what needs to be ran on the main thread but called from within the background workers.
If you are using the Backgroundworker-Class then you can use the ProgressChanged-Event. This approach does not block the backgroundworker thread with each send-call.
var driver = Periphrials.InitializeArduinoDriver();
StillMonitor = new BackgroundWorker();
StillMonitor.WorkerSupportsCancellation = true;
StillMonitor.WorkerReportsProgress = true;
StillMonitor.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (StillMonitor.CancellationPending)
{
break;
}
StillMonitor.ReportProgress(0); //Invokes the ProgressChanged Event on the thread the backgroundworker was created on.
} while (true);
});
StillMonitor.ProgressChanged += (sender, e) => {
(driver.Send(new DigitalReadRequest(properties.StillLowSwitch)).PinValue.ToString() == "Low")
}
If the thread should block with each send-call consider using the Dispatcher-Class (you need to add a reference to WindowsBase.dll):
using System.Windows.Threading; //WindowsBase.dll
//...
var driver = Periphrials.InitializeArduinoDriver();
Dispatcher driverDispatcher = Dispatcher.CurrentDispatcher; //Gets the Dispatcher for the current Thread (or creates it)
StillMonitor = new BackgroundWorker();
StillMonitor.WorkerSupportsCancellation = true;
StillMonitor.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (StillMonitor.CancellationPending)
{
break;
}
driverDispatcher.Invoke(new Action(() => { //Invoke and block the Dispatcher
(driver.Send(new DigitalReadRequest(properties.StillLowSwitch)).PinValue.ToString() == "Low")
}));
} while (true);
});
you can synchronize the driver.send call with a locking mechanism such as lock-statement.
/////class
{
private readonly object lockObject = new object();
....
///method
var driver = Periphrials.InitializeArduinoDriver();
StillMonitor = new BackgroundWorker();
StillMonitor.WorkerSupportsCancellation = true;
StillMonitor.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (StillMonitor.CancellationPending)
{ break; }
lock (lockObject)
{
//code here will run synchronously
(driver.Send(new DigitalReadRequest(properties.StillLowSwitch)).PinValue.ToString() == "Low")
}
} while (true);
});

C# .NET - Using Background Thread but GUI is Unresponsive

I'm implementing a Client who can ask a service for a certain action and also an abort button for this action.
Once I run the action using background thread the abort button should become active, but instead the entire GUI is stuck with the mouse icon as hour glass (Should mention that the action is still indeed occurring).
private void actionButton_Click(object sender, EventArgs e)
{
Run(RunMode.Action);
}
private void Run(RunMode runMode)
{
abortButton.Enabled = true;
try
{
var name = "ds_file";
var url = UrlProvider.BuildRequestUrl(runMode, name);
StartLoading($"Running request: {url}");
RunWorker(url);
}
catch (Exception ex)
{
AddToLog(ex.ToString());
PopError("Failed to run, see error in log box");
}
}
private void RunWorker(string url)
{
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) =>
{
DatabaseHelper.DisableAllJobs();
HttpRequestsHandler.HttpGet(url);
DatabaseHelper.EnableRegularJobs();
};
worker.RunWorkerCompleted += (sender, args) =>
{
StopLoading();
abortButton.Enabled = false;
if (args.Error != null)
{
PopError("Failed to run, see error in log box");
AddToLog(args.Error.ToString());
}
else
{
PopInfo("Completed successfully");
}
};
worker.RunWorkerAsync();
}
What am I doing wrong?
Thanks
Following example run background service every 10 seconds to update GUI. You can modify it as you wish. By running your thread as async task your GUI never get hang.
public frm_testform()
{
InitializeComponent();
dispatcherTimer_Tick().DoNotAwait();
}
private async Task dispatcherTimer_Tick()
{
DispatcherTimer timer = new DispatcherTimer();
TaskCompletionSource<bool> tcs = null;
EventHandler tickHandler = (s, e) => tcs.TrySetResult(true);
timer.Interval = TimeSpan.FromSeconds(10);
timer.Tick += tickHandler;
timer.Start();
while (true)
{
tcs = new TaskCompletionSource<bool>();
await Task.Run(() =>
{
// Run your background service and UI update here
await tcs.Task;
}
}
It indeed turns out I had controls.enable = false in some part of the code (I really thought it totally meant for something else), thank you all for your help!!

Pausing a new BackGroundWorker until previous completes

I am struggling with threading.
The problem is when I am iterating trough foreach loop.
When setting this.Document, the application performs login, that is triggered with an event and takes few seconds to complete. In the worker_RunWorkerCompleted method I need to perform some actions that depend on current login information.
The problem is that before I can perform this action for the first file, the this.Document already changes making the application perform another login. This way I can never actually perform my actions.
My question is: How can I pause the next thread until previous thread has completed.
Is there any other solution to my problem?
I tried with AutoResetEvent but I got no luck. I set waitOne() just after the RunWorkerAsync call and .Set() in the RunWorkerCompleted. The code never gets to RunWorkerCompleted...
Here is the code:
public void Start(object obj)
{
try
{
foreach (KeyValuePair<string, Stream> pair in this.CollectionOfFiles)
{
Worker = new BackgroundWorker();
Worker.DoWork += new DoWorkEventHandler(worker_DoWork);
Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
using (Stream stream = pair.Value)
{
primaryDocument = new Document(stream);
DataHolderClass dataHolder = new DataHolderClass();
dataHolder.FileName = pair.Key;
dataHolder.Doc = secondaryDocument;
//background thread call
Worker.RunWorkerAsync(dataHolder);
}
}
}
catch (Exception ex)
{
// exception logic
}
finally
{
// complete logic
}
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
DataHolderClass dataHolder = ((DataHolderClass)e.Argument);
// setting this attribute triggers execution of login event
this.Document = dataHolder.Doc;
e.Result = (dataHolder);
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// here I need to perform some actions that are depending on the current login
DataHolderClass dataHolder = ((DataHolderClass)e.Result);
this.eventAggregator.GetEvent<ActionEvent>().Publish(new Message(EMessageType.Info) { Title = dataHolder.FileName });
}
no9,
Try the following:
System.Threading.ManualResetEvent _busy = new System.Threading.ManualResetEvent(false);
void ResumeWorker()
{
// Start the worker if it isn't running
if (!backgroundWorker1.IsBusy) backgroundWorker1.RunWorkerAsync(dataHolder);
// Unblock the worker
_busy.Set();
}
void PauseWorker()
{
// Block the worker
_busy.Reset();
}
void CancelWorker()
{
if (backgroundWorker1.IsBusy) {
// Set CancellationPending property to true
backgroundWorker1.CancelAsync();
// Unblock worker so it can see that
_busy.Set();
}
}
then in your code run the method.
Let me know if it works :)
class SimpleWaitPulse
{
static readonly object _locker = new object();
static bool _go;
static void Main()
{ // The new thread will block
new Thread (Work).Start(); // because _go==false.
Console.ReadLine(); // Wait for user to hit Enter
lock (_locker) // Let's now wake up the thread by
{ // setting _go=true and pulsing.
_go = true;
Monitor.Pulse (_locker);
}
}
static void Work()
{
lock (_locker)
while (!_go)
Monitor.Wait (_locker); // Lock is released while we’re waiting
Console.WriteLine ("Woken!!!");
}
}
Can you use pulse ?
Taken from : Threading in C# from albahari.com
Well, the design is terrible... but if you need to stick to it, you can set wait handles in a previous worker and wait for it in next. This is the minimal fix, still quite an abomination:
public void Start(object obj)
{
try
{
BackgroundWorker previousWorker = null;
DataHolderClass previousWorkerParams = null;
foreach (KeyValuePair<string, Stream> pair in this.CollectionOfFiles)
{
// signal event on previous worker RunWorkerCompleted event
AutoResetEvent waitUntilCompleted = null;
if (previousWorker != null)
{
waitUntilCompleted = new AutoResetEvent(false);
previousWorker.RunWorkerCompleted += (o, e) => waitUntilCompleted.Set();
// start the previous worker
previousWorker.RunWorkerAsync(previousWorkerParams);
}
Worker = new BackgroundWorker();
Worker.DoWork += (o, e) =>
{
// wait for the handle, if there is anything to wait for
if (waitUntilCompleted != null)
{
waitUntilCompleted.WaitOne();
waitUntilCompleted.Dispose();
}
worker_DoWork(o, e);
};
using (Stream stream = pair.Value)
{
primaryDocument = new Document(stream);
DataHolderClass dataHolder = new DataHolderClass();
dataHolder.FileName = pair.Key;
dataHolder.Doc = secondaryDocument;
// defer running this worker; we don't want it to finish
// before adding additional completed handler
previousWorkerParams = dataHolder;
}
previousWorker = Worker;
}
if (previousWorker != null)
{
previousWorker.RunWorkerAsync(previousWorkerParams);
}
}
catch (Exception ex)
{
// exception logic
}
finally
{
// complete logic
}
}

Exception: The application called an interface that was marshalled for a different thread

private void LogInButton_Click(object sender, RoutedEventArgs e)
{
var api = new RestAPI("http://localhost:2624/", UsernameTextBox.Text, PasswordTextBox.Password);
api.AutenticarUsuarioFinalizado += (o, args) =>
{
ProgressBar.IsIndeterminate = false;
ProgressBar.Visibility = Visibility.Collapsed;
LogInButton.IsEnabled = true;
if (args.Error) return;
if (args.Resultado.Autenticado)
{
}
};
api.AutenticarUsuario();
ProgressBar.Visibility = Visibility.Visible;
ProgressBar.IsIndeterminate = true;
LogInButton.IsEnabled = false;
}
api.AutenticarUsuario(); calls a rest API asynchronously, when it's done it calls the event handler api.AutenticarUsuarioFinalizado and got this error in line ProgressBar.IsIndeterminate = false; because the call open a new thread, how can I fix it? the error is:
The application called an interface that was marshalled for a different thread.
The problem is that your event handler doesn't execute on the UI thread. I think the best way to fix that is to convert your EAP (Event-based Asynchronous Pattern) method to TAP (Task-based Asynchronous Pattern) using TaskCompletionSource:
public static Task<Resultado> AutenticarUsuarioAsync(this RestAPI api)
{
var tcs = new TaskCompletionSource<Resultado>();
api.AutenticarUsuarioFinalizado += (sender, args) =>
{
if (args.Error)
tcs.TrySetException(new SomeAppropriateException());
else
tcs.TrySetResult(args.Resultado);
};
api.AutenticarUsuario();
return tcs.Task;
}
…
private async void LogInButton_Click(object sender, RoutedEventArgs e)
{
var api = new RestAPI(
"http://localhost:2624/", UsernameTextBox.Text,
PasswordTextBox.Password);
ProgressBar.Visibility = Visibility.Visible;
ProgressBar.IsIndeterminate = true;
LogInButton.IsEnabled = false;
try
{
var resultado = await api.AutenticarUsuarioAsync();
if (resultado.Autenticado)
{
// whatever
}
}
catch (SomeAppropriateException ex)
{
// handle the exception here
}
finally
{
ProgressBar.IsIndeterminate = false;
ProgressBar.Visibility = Visibility.Collapsed;
LogInButton.IsEnabled = true;
}
}
Because awaiting a Task will always resume on the original context, you're not going to get an exception this way. As an additional advantage, you don't have to write your code “inside-out”, like you do with EAP.
You should also consider using bindings, instead of setting the properties of your UI controls manually.
Your AutenticarUsuarioFinalizado event handler is probably being executed in a thread different from the UI thread. You should only modify/create UI objects from the UI thread. Use a dispatcher in your event handler, check here: Run code on UI thread in WinRT
var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
api.AutenticarUsuarioFinalizado += (o, args) =>
{
dispatcher.RunAsync(DispatcherPriority.Normal, () =>
ProgressBar.IsIndeterminate = false;
ProgressBar.Visibility = Visibility.Collapsed;
[...]

Categories