Asynchronous Thread Lifetime & WPF UI Update - c#

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.

Related

Allow user to immediately cancel long-running WPF operation from button

In a WPF app, we have a button that the user can click to trigger a list of videos to be loaded into VLC Media Player:
<Button Content="{Binding RotatorButtonLabel}" Command="{Binding RotateVideosCommand}" />
In the view model MainWindowVm, we have the command to handle the button click:
public ICommand RotateVideosCommand => new RelayCommand(RotateVideos);
private void RotateVideos()
{
IsRotatorActive = !IsRotatorActive;
RotatorButtonLabel = IsRotatorActive
? "Stop Rotator"
: "Rotate Videos";
_rotatorVm = new RotatorVm
{
ImageVms = ImagesView.Cast<ImageVm>().ToList(),
IsRotatorActive = IsRotatorActive
};
// This fires off a new thread to run the rotator, otherwise the UI freezes.
Task.Run(() => Messenger.Default.Send(rotatorVm, "LaunchRotator"));
}
Note in the above command handler, we use the MVVM Light Toolkit's Messenger to tell the code-behind to launch the rotator.
Now in the MainWindow.xaml.cs, we have the following c'tor:
private CancellationTokenSource _cancellationTokenSource = null;
private CancellationToken _cancellationToken;
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<RotatorVm>(this, "LaunchRotator", LaunchRotator);
// Other logic...
}
And then this what the above LaunchRotator calls:
private void LaunchRotator(RotatorVm rotatorVm)
{
if (_cancellationToken.IsCancellationRequested)
{
_cancellationTokenSource.Dispose();
}
if (_cancellationTokenSource == null || _cancellationToken.IsCancellationRequested)
{
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
}
if (!rotatorVm.IsRotatorActive)
{
_cancellationTokenSource.Cancel();
return;
}
RotateVideos();
}
private void RotateVideos()
{
while (true)
{
if (_cancellationToken.IsCancellationRequested)
{
return;
}
// This is to simplify the code and simulate work.
Thread.Sleep(5000);
}
}
If I click the "Stop Rotator" button, it can take a few seconds for the code to hit the next iteration of the while loop and read the IsCancellationRequested. How do I make it stop immediately in this scenario?
I've looked at this example, but it's assuming the task and the activities are all in one class; here, I have a view model and a code-behind. Thank you.
You can't (in a practical way) and shouldn't.
If you want to stop work in another thread then the proper way is to signal the thread (like you've done) and allow the thread to stop on it's own. Since your example workload is a Thread.Sleep(5000), once it's hit, that thread cannot do anything else until the sleep has expired. In other words, you can signal the thread properly and if it is sleeping, it will live until the sleep is complete, and then it will check the signal again.
Options:
In this case you can pass the Token to the simulated workload
using Task.Wait(5000, token) and using this instead of
Thread.Sleep(5000). This way the simulated work can also be
canceled.
For real work; you have to use your best judgement to test the signal
when and where it looks fair so that you're not waiting a long period
of time to return when cancel is signaled. Just note that making the caller wait while the
Thread or Task properly ends itself is the best way to do
it. This is why Thread.Abort gets so much criticism and is
discouraged.
Another option is to fire and forget. You can call Cancel() on the token source and then move on without waiting for the Task or Thread to complete. You have to design with this in mind but it's practical.

c# Async/Await not updating UI

I am learning to implement async/await on one of my task which takes a long time to complete. Once the task is completed, it should update the DGV.
private async void frmA_DragDrop(object sender, DragEventArgs e)
{
// Some codes to accept dropped files
// Run task asynchronously
Task<DataTable> task = Task.Run(() => PDF.CheckPDFs(Files, tblPDFs));
dgvPDF.DataSource = await task;
}
I found that after the task is completed, it is not updating the DGV. So, I added the InvokeRequired bit to manually update the table and also to prevent cross-thread exception.
// Update UI
if (dgvPDF.InvokeRequired)
{
dgvPDF.Invoke((MethodInvoker)(() => { dgvPDF.Refresh(); }));
}
else
{
dgvPDF.Refresh();
}
I also have a separate event handler which highlights the row red is a file is invalid:
private void dgvPDF_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
if (dgvPDF["Pages",e.RowIndex].Value.ToString() == "0")
dgvPDF.Rows[e.RowIndex].DefaultCellStyle.BackColor = System.Drawing.Color.MistyRose;
}
What happened is that the RowAdded event handler is not triggered at all. If I made the code synchronous i.e removing await/async and invoke, everything works fine except with the UI freezing
My question is using InvokedRequired the correct way to update the UI in async await? What can I do to fix this issue?
no, you don't have to "invoke", the context will switch back after async method call complete, i.e., the statements after await will get executed in UI thread.

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

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());

How do I force a task to run on the UI thread?

Original message below. Let me try and explain with more details why I am asking for this.
I have a page that listens to the Share charm request:
void Page_Loaded(object sender, RoutedEventArgs e)
{
m_transferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.GetForCurrentView();
m_transferManager.DataRequested += TransferManager_DataRequested;
}
When the event fires (TransferManager_DataRequested) it does not fire on the UI thread:
void TransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var data = args.Request.Data;
// More related stuff omitted - not important.
data.SetDataProvider(StandardDataFormats.Bitmap, GetImage_DelayRenderer);
}
Now, when GetImage_DelayRender is called, it also does not get called on the UI thread. However, in it, I need to do a bunch of UI related things. Specifically, I need to call a method that only works on the UI (it's a method I use elsewhere and I want to reuse it's logic). The method is called GetImageAsync and it needs to run on the UI because it does a bunch of interactions with WriteableBitmap. It also does a bunch of async operations (such as writing to stream etc) which is why it's async. I block the UI on GetImageAsync() for as short a time as I can.
Here's what GetImage_DelayRender looks like:
private async void GetImage_DelayRenderer(DataProviderRequest request)
{
var deferral = request.GetDeferral();
await Dispatcher.RunTask(async () => // RunTask() is an extension method - described in the original question below.
{
try
{
var bitmapStream = await GetImageAsync();
request.SetData(RandomAccessStreamReference.CreateFromStream(bitmapStream));
}
catch
{
}
});
deferral.Complete();
}
What I want to know is, what is the most correct way to achieve the call to Dispatcher.RunTask() above (which is my hack extension method).
----- START ORIGINAL MESSAGE -------
Say I have the following task:
private async Task SomeTask()
{
await Task.Delay(1000);
// Do some UI and other stuff that may also be async
}
Edit (Clarification): I do not want to block the UI. The task I want to execute (even in the example, if you read it) WILL NOT block the UI. I just want the task to run in the context of the UI for it's synchronous portions.
I want to run this on code on the UI thread as an Async operation. Dispatcher.RunXXX() methods take an action, which means they will run the action and notify you when they are done. That's not good enough. I need the entire task to run on the UI thread (as it would have executed had I run it from the UI thread) and then, when done, to notify me back.
The only way I could think of, is to use the Dispatcher.RunXXX() methods to execute an anon delegate that sets a local variable in my method to the task and then awaits that...
public async static Task RunTask(this CoreDispatcher dispatcher, Func<Task> taskGiver)
{
Task task = null;
await dispatcher.RunAsync(() => task = taskGiver());
await task;
}
This looks pretty damn ugly. Is there a better way of doing it?
Edit2: Guys - read this code - if I execute the first code block above using the RunTask() hack I have, IT WILL NOT BLOCK THE UI on the Task.Delay()...
I want to run this on code on the UI thread as an Async operation.
Then just run it:
async void MyEventHandler(object sender, ...)
{
await SomeTask();
}
Update:
I'm not sure this is a "legal" operation, but you can schedule that method to run on the UI thread by capturing the CoreDispatcher while the UI is active and later calling RunAsync:
private async void GetImage_DelayRenderer(DataProviderRequest request)
{
var deferral = request.GetDeferral();
Task task = null;
await coreDispatcher.RunAsync(() => { task = SomeTask(); });
await task;
deferral.Complete();
}
I don't have time to do a complete solution, so hopefully you will still find this useful...
First, as others have pointed out, you cannot run something on the UI thread and not have it block the UI thread. End of discussion. What you are saying you need is something to run on a non-UI thread and periodically notify the UI thread that there are updates that need to be processed.
To accomplish this, you need something like this...
public class LongTask
{
public event EventHandler MyEvent;
public void Execute()
{
var task = Task.Factory.StartNew(() =>
{
while (true)
{
// condition met to notify UI
if (MyEvent != null)
MyEvent(this, null);
}
});
}
}
In your UI then, do something like...
private void button_Click(object sender, RoutedEventArgs e)
{
var test = new LongTask();
test.MyEvent += test_MyEvent;
test.Execute();
}
void test_MyEvent(object sender, EventArgs e)
{
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
test.Text += " bang ";
});
You could obviously implement this in a much cleaner fashion using something like MVVM, but this is the basic idea.
}
I've done it like this:
public static Task<string> GetResultAsync()
{
return Task<string>.Factory.StartNew(() => GetResultSync());
}
In UI:
private async void test()
{
string result = await GetResultAsync();
// update UI no problem
textbox.Text = result;
}

Why thread in background is not waiting for task to complete?

I am playing with async await feature of C#. Things work as expected when I use it with UI thread. But when I use it in a non-UI thread it doesn't work as expected. Consider the code below
private void Click_Button(object sender, RoutedEventArgs e)
{
var bg = new BackgroundWorker();
bg.DoWork += BgDoWork;
bg.RunWorkerCompleted += BgOnRunWorkerCompleted;
bg.RunWorkerAsync();
}
private void BgOnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
{
}
private async void BgDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
await Method();
}
private static async Task Method()
{
for (int i = int.MinValue; i < int.MaxValue; i++)
{
var http = new HttpClient();
var tsk = await http.GetAsync("http://www.ebay.com");
}
}
When I execute this code, background thread don't wait for long running task in Method to complete. Instead it instantly executes the BgOnRunWorkerCompleted after calling Method. Why is that so? What am I missing here?
P.S: I am not interested in alternate ways or correct ways of doing this. I want to know what is actually happening behind the scene in this case? Why is it not waiting?
So, BgDoWork is called on a background thread by the BackgroundWorker
It calls Method, which starts the loop and calls http.GetAsync
GetAsync returns a Task and continues it's work on another thread.
You await the Task which, because the Task has not completed, returns from Method
Similarly, the await in BgDoWork returns another Task
So, the BackgroundWorker sees that BgDoWork has returned and assumes it has completed.
It then raises RunWorkerCompleted
Basically, don't mix BackgroundWorker with async / await!
Basically, there are two problems with your code:
BackgroundWorker wasn't updated to work with async. And the whole point of async methods is that they actually return the first time they await something that's not finished yet, instead of blocking. So, when your method returns (after an await), BackgroundWorker thinks it's completed and raises RunWorkerCompleted.
BgDoWork() is an async void method. Such methods are “fire and forget”, you can't wait for them to complete. So, if you run your method with something that understands async, you would also need to change it to async Task method.
You said you aren't looking for alternatives, but I think it might help you understand the problem if I provided one. Assuming that BgDoWork() should run on a background thread and BgOnRunWorkerCompleted() should run back on the UI thread, you can use code like this:
private async void Click_Button(object sender, RoutedEventArgs e)
{
await Task.Run((Func<Task>)BgDoWork);
BgOnRunWorkerCompleted();
}
private void BgOnRunWorkerCompleted()
{
}
private async Task BgDoWork()
{
await Method();
}
Here, Task.Run() works as an async-aware alternative to BackgroundWorker (it runs the method on a background thread and returns a Task that can be used to wait until it actually completes). After await in Click_Button(), you're back on the UI thread, so that's where BgOnRunWorkerCompleted() will run. Click_Button() is an async void method and this is pretty much the only situation where you would want to use one: in an event handler method, that you don't need to wait on.
I think you need some reason for the background thread to stay alive while it's waiting for Method() to complete. Having an outstanding continuation is not enough to keep a thread alive, so your background worker terminates before Method() completes.
You can prove this to yourself by changing your code so that the background thread does a Thread.Sleep after the await Method(). That's almost certainly not the real behaviour you want, but if the thread sleeps for long enough you'll see Method() complete.
Following is how DoWork is raised and handled. (code retrieved using Reflector tool).
private void WorkerThreadStart(object argument)
{
object result = null;
Exception error = null;
bool cancelled = false;
try
{
DoWorkEventArgs e = new DoWorkEventArgs(argument);
this.OnDoWork(e);
if (e.Cancel)
{
cancelled = true;
}
else
{
result = e.Result;
}
}
catch (Exception exception2)
{
error = exception2;
}
RunWorkerCompletedEventArgs arg = new RunWorkerCompletedEventArgs(result, error, cancelled);
this.asyncOperation.PostOperationCompleted(this.operationCompleted, arg);
}
protected virtual void OnDoWork(DoWorkEventArgs e)
{
DoWorkEventHandler handler = (DoWorkEventHandler) base.Events[doWorkKey];
if (handler != null)
{
handler(this, e);
}
}
There is no special handling to wait for async method. (using async/await keyword).
To make it wait for async operation, following changes are required.
async private void WorkerThreadStart(object argument)
await this.OnDoWork(e);
async protected virtual void OnDoWork(DoWorkEventArgs e)
await handler(this, e);
But then, BackgroundWorker is .net 2.0 construct, and async/await are .net 4.5. it will be full circle, if any one of these uses other construct.
You can't await an event handler because it doesn't return anything to await on. From the documentation of the async keyword:
The void return type is used primarily to define event handlers, where a void return type is required. The caller of a void-returning async method can't await it and can't catch exceptions that the method throws.
By adding the async keyword to the BgDoWork event handler you are instructing .NET to execute the handler asynchronously and return as soon as the first yielding operation is encountered. In this case, this happens after the first call to http.GetAsync

Categories