Threading.task and Dispatcher.Invoke method in the Window Close event - c#

I want to perform series of operations synchronously while closing my MVVM based WPF Application.
Right now I am using Task and Dispatcher.Invoke within the tasks to show the message to the user.
The issue is that when i used Dispatcher.Invoke method in the myfunction function, application gets stuck there. I know this function is working properly when I used these function other than closed event.
So is there any issue of using the Dispatcher.Invoke method in the Close event os the application. How can i solve this?
/// <summary>
/// Main window closing
/// </summary>
private void MainWindowClosing(object args)
{
var task1 = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
myfunction();
}).ContinueWith((cc) => { });
task1.Wait();
}
private void myfunction()
{
//my serries of operation will come here.
System.Windows.Application.Current.Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show("test");
}));
}

You are creating a Deadlock here. It won't work even when you put the code in button click handler.
REASON
You are creating a task and waiting on task using task1.Wait(). Hence UI dispatcher is waiting for task to complete before it can process any further messages.
At same time you are invoking one operation on UI dispatcher from task that too synchronously here
App.Current.Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show("test");
}));
but since UI dispatcher is still waiting on task to complete, you can't invoke any method synchronously on it. Hence the DEADLOCK (waiting on each other).
Possible Solution
If you want to invoke task synchronously and that too without closing window, you can achieve that using following steps:
First of all remove the task1.Wait(). (do not block UI dispatcher)
Second, maintain bool to keep count that close event has been initiated.
Last, cancel closing event by setting e.Cancel = true and manually raise close event from task itself once you finished.
Relevant code:
bool closeInitiated = false;
private void poc_Closing(object sender, CancelEventArgs e)
{
if (!closeInitiated)
{
var task1 = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
myfunction();
}).ContinueWith((cc) => { });
closeInitiated = true;
e.Cancel = true;
}
}
private void myfunction()
{
App.Current.Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show("test");
Close();
}));
}

I think the issue is at task1.Wait(); Go through this UI Thread Wait and DeadLock
I hope this will help.

I have gotten the same issue, and I figured the problem was that the Task where I'm invoking to, was already closed a second after clicking the Close button, so when the Invoking function takes a bit longer - it might crash.
If you still wanna execute this function, I used BeginInvoke instead of Invoke and it worked.
Here is my code:
if (app.Current.Dispatcher.CheckAccess()) //doesn't need to be invoked
myFunction();
else //coming from another thread need invoker
{
//at exiting program this task might already be canceled, so make sure it's not
if (!app.Current.Dispatcher.HasShutdownFinished)
app.Current.Dispatcher.Invoke(myFunction());
else
app.Current.Dispatcher.BeginInvoke(myFunction());

Related

C# backgroundworker RunworkerCompleted vs async await

Updated with answers:
The true way of wait until a number of different tasks to be finished would need async await instead of background worker.
#
I know there are numerous discussion about backgroundworker but I've being searched around and cannot find the answer.
Here is my code example(basic logic, the actual code is much longer), I wonder if there is a way to get around this:
BackgroundWorker MCIATS1Worker = new BackgroundWorker();
private AutoResetEvent _MCIATS1WorkerResetEvent = new AutoResetEvent(false);
public MainWindow()
{
InitializeComponent();
MCIATS1Worker = new BackgroundWorker();
MCIATS1Worker.DoWork += new DoWorkEventHandler(MCIATS1Worker_DoWork);
MCIATS1Worker.WorkerReportsProgress = true;
MCIATS1Worker.WorkerSupportsCancellation = true;
MCIATS1Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(MCIATS1_RunWorkerCompleted);
for (int i = 1; i <= 10; i++)
{
//some code
MCIATS1Worker.RunWorkerAsync();
_MCIATS1WorkerResetEvent.WaitOne();
}
}
DoWork and runworkercompleted
void MCIATS1Worker_DoWork(object sender, DoWorkEventArgs e)
{
//do something here
}
void MCIATS1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("hello world");
_MCIATS1WorkerResetEvent.Set();
}
For some reasons, the MCIATS1_RunWorkerCompleted won't be triggered until the loop finished. And apparently the WaitOne is holding the loop.
Here is my question,
why RunWorkerCompleted won't be trigger the RunWorkerCompleted when the worker is actually finished the work?
Thank you.
###UPDATED SOLUTION
This is the right way of doing it.
private async void WhateverFunction()
{
await Task.WhenAll(MCIATS1WorkerDoWorkAsync(param),...other tasks);
}
private Task MCIATS1WorkerDoWorkAsync(bkgWorkParameter param)
{
return Task.Run(() =>
{
//Do whatever
});
}
It happens because when you use a BackgroundWorker it's RunWorkerCompleted event is posted to the SynchronizationContext of the thread that called RunWorkerAsync.
Because you call RunWorkerAsync on the UI thread the event can't run until the UI thread starts processing new messages in the message loop. However you prevented the UI thread from returning to the message loop by your _MCIATS1WorkerResetEvent.WaitOne(); call.
So what it boils down to is _MCIATS1WorkerResetEvent.Set(); is waiting for MCIATS1_RunWorkerCompleted to fire to stop blocking and MCIATS1_RunWorkerCompleted is waiting for _MCIATS1WorkerResetEvent.Set(); to stop blocking the UI thread so it's message to be processed.
Both things are waiting for the other to complete before itself completes and you have a classic deadlock.
There is no need for a for loop for this problem to happen, this same problem would happen with or without out the loop, in fact the loop never gets to run it's 2nd itteration because it will have deadlocked on the first time through so it does not matter that there is a loop at all.
Depend on what kind of work your MCIATS1Worker_DoWork method do, you can consider to use async-await approach, which makes code a little bid more cleaner.
private async Task MCIATS1WorkerDoWorkAsync()
{
await Task.Delay(1000) // do something asynchronously for 1 second
}
private async void MainWindow_Load(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
//some code
await MCIATS1WorkerDoWorkAsync();
MessageBox.Show("hello world");
}
}
Message box will be shown 10 times every 1 second. await keyword will continue loop only after MCIATS1WorkerDoWorkAsync method has successfully finished.
With async-await your form will remain responsive and if DoWork method do some IO operations, then you will not start another thread (as BackgroundWorker do) and whole execution will happens on one thread.

How to safely show messagebox in multi-thread application?

I have something doing background and I want to show a messagebox if something wrong happens.
First I tried
var _timer = new System.Threading.Timer((o) =>
{
if(!DoCheck()){
Messagebox.Show("The message");
}
});
Nothing wrong happens.
And I have another job to be done in background, and it's invoked by button click, like
private void button3_Click(object sender, EventArgs e)
{
var task = new Task(() =>
{
DoWork();
Messagebox.Show("Done");
});
_task.Start();
}
A System.Reflection.TargetInvocationException is thrown when the MessageBox is shown.
I have also tried this.Invoke, it raised an exception, too.
My question is:
Is the first case safe?
How to make the second case work?
No. You should preferably be using System.Windows.Forms.Timer in a WinForms application. The documentation specifically calls this out:
This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread, or marshal the call onto another thread.
Furthermore, it depends on what your DoCheck method is doing. We will need to see the code of that method.
Use the BeginInvoke method:
var form = this;
var task = new Task(() =>
{
DoWork();
form.BeginInvoke(() =>
{
MessageBox.Show("Done");
});
});

WPF, STA thread exception

I'm working on a WPF (MVVM) app.
Using 2 buttons, one to load data from db and another one to delete a selected item.
When clicking the Load button, a LoadCommand is fired and calls the StartLoadingThread
private void StartLoadingThread()
{
ShowLoadProcessing(); // show some text on top of screen ("Loading in progress...")
ThreadStart ts = delegate
{
LoadMyitems();
System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (EventHandler)
delegate
{
HideLoadProcessing(); // hide the text "Loading in progress..."
}, null, null);
};
ts.BeginInvoke(ts.EndInvoke, null);
}
Works fine, now when I select an item and click the Delete button, the DeleteItemCommand is fired and calls the StartDeletingThread
private void StartDeletingThread()
{
ShowDeleteProcessing(); // Show on top of screen "Deleting in progress..."
ThreadStart ts = delegate
{
DeleteSelectedItem();
System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (EventHandler)
delegate
{
HideDeletingProcessing();
}, null, null);
};
ts.BeginInvoke(ts.EndInvoke, null);
}
When StartDeletingThread is started, I'm getting the following exception:
{"The calling thread must be STA, because many UI components require this."}
I think you want something like this, though I am a bit newbie in WPF, you will have to replace this.BeginInvoke with something what is doing same in WPF (Dispatcher). Also code may not compile (add Action type conversion for Thread?), but idea is to simply start thread (you invoke it, for some reasons, why?) and in that thread invoke UI operation after deleting.
private void StartDeletingThread()
{
ShowDeleteProcessing(); // Show on top of screen "Deleting in progress..."
new Thread(() =>
{
DeleteSelectedItem();
this.BeginInvoke(() => HideDeletingProcessing());
}.Start();
}
Try invoking StartDeletingThread using the application dispatcher like this:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,new
Action(()=>StartDeletingThread())
private void StartDeletingThread()
{
ShowDeleteProcessing(); // Show on top of screen "Deleting in progress..."
Task.Run(() =>
{
DeleteSelectedItem();
Application.Current.Dispatcher.BeginInvoke((Action)HideDeletingProcessing);
});
}
probably will blow up at DeleteSelectedItem();. I know we all love to show progress bars for actions that take less than a second, but why bother when it leads to questions on StackOverflow?
private void StartDeletingThread()
{
DeleteSelectedItem();
}
Done and done. I seriously doubt it takes a long time to delete the selected item.
If, in some rare case it does... then you need to find out in DeleteSelectedItem where the UI is getting touched, and use the application's dispatcher to do the touching.
(Side note, here's how you'd safely multithread in 4.5, using async/await... safe, as long as you understand the repercussions of async void, that is)
private async void StartDeletingThread()
{
// we're in the UI thread
ShowDeleteProcessing();
await DeleteSelectedItem();
// back in the UI thread
HideDeletingProcessing();
}
private Task DeleteSelectedItem()
{
// doing the work on a Task thread
return Task.Run(() => DeleteSelectedItem = null);
}

Asynchronous Thread Lifetime & WPF UI Update

On button click I have fired call to StartContinuousThread which keeps polling the server every one second.
public class ThreadsWindow
{
CancellationTokenSource wtoken = new CancellationTokenSource();
private void btnStartTest_Click(object sender, RoutedEventArgs e)
{
StartContinuousThread();
}
void StartContinuousThread()
{
while(true)
{
var fact = Task.Factory.StartNew(() =>
{
CallServer();
Task.Delay(1000, wtoken.Token);
},
wtoken.Token);
}
}
}
StartContinuousThread starts executing, but btnStartTest_Click event handler finishes its execution.
How StartContinuousThread method would be able to update UI in this
case?
I wonder whether StartContinuousThread is also terminated with event handler, since there is no wait keyword for re-joining.
Please help!
If you goal is to poll the server every second you have a number of problem.
There is no loop. You execute the method once and then stop.
You create a task using Delay and then ignore it. You ought to be creating a continuation of that task to do the rest of the work to actually not do anything for a second.
Here is an implementation that addresses those issues:
private async Task StartContinuousThread(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Run(() => CallServer());
await Task.Delay(1000, token);
}
}
Another possibility, especially if you're using an older version of C#, would be to use a timer to run some code every second.
As for updating the UI; you can do so freely anywhere outside of the call to Task.Run in this example. In your example you'd need to use some mechanism to marshal back to the UI thread, such as capturing the UI's synchronization context and posting to it.

How to asynchronously wait for x seconds and execute something then?

I know there is Thread.Sleep and System.Windows.Forms.Timer and Monitor.Wait in C# and Windows Forms. I just can't seem to be able to figure out how to wait for X seconds and then do something else - without locking the thread.
I have a form with a button. On button click a timer shall start and wait for 5 seconds. After these 5 seconds some other control on the form is colored green. When using Thread.Sleep, the whole application would become unresponsive for 5 seconds - so how do I just "do something after 5 seconds"?
(transcribed from Ben as comment)
just use System.Windows.Forms.Timer. Set the timer for 5 seconds, and handle the Tick event. When the event fires, do the thing.
...and disable the timer (IsEnabled=false) before doing your work in oder to suppress a second.
The Tick event may be executed on another thread that cannot modify your gui, you can catch this:
private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
private void StartAsyncTimedWork()
{
myTimer.Interval = 5000;
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Start();
}
private void myTimer_Tick(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
/* Not on UI thread, reenter there... */
this.BeginInvoke(new EventHandler(myTimer_Tick), sender, e);
}
else
{
lock (myTimer)
{
/* only work when this is no reentry while we are already working */
if (this.myTimer.Enabled)
{
this.myTimer.Stop();
this.doMyDelayedWork();
this.myTimer.Start(); /* optionally restart for periodic work */
}
}
}
}
Just for completeness: with async/await, one can delay execute something very easy (one shot, never repeat the invocation):
private async Task delayedWork()
{
await Task.Delay(5000);
this.doMyDelayedWork();
}
//This could be a button click event handler or the like */
private void StartAsyncTimedWork()
{
Task ignoredAwaitableResult = this.delayedWork();
}
For more, see "async and await" in MSDN.
more completeness:
Depending on your Framework, there is a good chance you will have DispatcherTimer class that can handle the invocation internally (WPF-variants). (finde details in ms docs)
Have you tried
public static Task Delay(
int millisecondsDelay
)
You can use like this:
await Task.Delay(5000);
reference: https://msdn.microsoft.com/en-us/library/hh194873(v=vs.110).aspx
You can start an asynchronous task that performs your action:
Task.Factory.StartNew(()=>
{
Thread.Sleep(5000);
form.Invoke(new Action(()=>DoSomething()));
});
[EDIT]
To pass the interval in you simply have to store it in a variable:
int interval = 5000;
Task.Factory.StartNew(()=>
{
Thread.Sleep(interval);
form.Invoke(new Action(()=>DoSomething()));
});
[/EDIT]
You can wait UI thread the way you want it to work.
Task.Factory.StartNew(async() =>
{
await Task.Delay(2000);
// it only works in WPF
Application.Current.Dispatcher.Invoke(() =>
{
// Do something on the UI thread.
});
});
if you're using .Net Framework 4.5 or higher version, you can use Task.Run instead of Task.Factory.StartNew just like below.
int millisecondsDelay = 2000;
Task.Run(async() =>
{
await Task.Delay(millisecondsDelay);
// it only works in WPF
Application.Current.Dispatcher.Invoke(() =>
{
// Do something on the UI thread.
});
});
You are looking at it wrong.
Click the button, it kicks off a timer with an interval of x seconds. When those are up it's eventhandler executes the task.
So what don't you want to happen.
While the x seconds are elapsing.?
While The task is executing?
If for instance it's you don't want the button to be clicked until delay and task are done. Disable it in the button click handler, and enable it on task completion.
If all you want is a five second delay prior to the task, then you should pass the start delay to the task and let it take care of it.
your application hangs because you are invoking the 5 second sleep/wait on the main UI thread. put the sleep/wait/whatever action in a separate thread (actually System.Windows.Forms.Timer should do that for you) and when it completes invoke the action that turns some control green. remember to check InvokeRequired. here's a short sample (SetText can be called from another thread, if it is the call will instead be invoked on the main UI thread where the textbox is on):
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
I took the sample from here (well worth a read!).
#eFloh in the post marked as answer said:
The Tick event may be executed on another thread that cannot modify
your gui, you can catch this ...
That is not what the docs say.
You are using a System.Windows.Forms.Timer in your example code.
That is a Forms.Timer.
According to the C# docs the Timer events are raised on the UI thread.
This Windows timer is designed for a single-threaded environment where
UI threads are used to perform processing. It requires that the user
code have a UI message pump available and always operate from the same
thread ...
Also see stackoverflow post here

Categories