I have a winforms app with a long running task and two buttons.
One button Start and one Stop.
A new task with a cancellation Token starts when I press the start button.
And if I press the Stop button the cancellation Token's Cancel Method gets called.
I want the UI to be usable during all the time so where do I put the try, catch block for this job. In all the examples I saw they put it around t.Wait();
But if I do that the UI freezes and that is the reason why I used a Task in the first place, to continue using the ui while doing the task. So where to put the try catch block without using Task.Wait.
Start button:
tokenSource2 = new CancellationTokenSource();
ct = tokenSource2.Token;
t = new Task(doStart, ct);
t.Start();
Stop button:
tokenSource2.Cancel();
You could update doStart to handle the cancellation event and exit the task gracefully so you wouldn't need to use Wait at all e.g.
public void doStart(CancellationToken token)
{
while(...)
{
...
if (token.IsCancellationRequested)
break;
}
}
Alternatively, you could wait for the task result on another thread e.g.
Thread.QueueUserWorkItem((state) =>
{
try
{
t.Wait();
}
catch(...)
{
...
}
});
Related
I have application for controlling LED strip. UI has combobox with effect selection and when user selects mode, it waits for currently running effect loop to finish by calling StopTask() and then executes selected effect. It sends LED color etc. to Arduino via serial. This works.
Problem is when I trigger StopTask() by MainWindow_OnClosing (when user exits the application), it triggers StopTask() but gets stuck on await currentEffectMode. I will try to explain it more by comments inside the code
MainWindow mode selection:
private void CbMode_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Checkbox selection triggers this
_ledStrip.LightModes.ChangeMode(CbMode.SelectedIndex);
}
private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
// Trigger disconnect and wait for success - this doesn't work (explained below in code comments)
_ledStrip.LightModes.Disconnect().Wait();
}
Light modes class:
private Task _modeTask;
private CancellationTokenSource _cancellationToken = new CancellationTokenSource();
// This is being triggered by change mode
internal async void ChangeMode(int mode)
{
// It waits for current loop to finish
await StopTask();
switch (mode)
{
case (int)Modes.Static:
// Then assigns new one
_modeTask = Static(_cancellationToken.Token);
break;
case (int)Modes.Breath:
_modeTask = Breath(_cancellationToken.Token);
break;
}
}
internal async Task StopTask()
{
if (_modeTask == null)
return;
// Set cancellation token to cancel
_cancellationToken.Cancel();
try
{
// and wait for task to finish. This works if triggered by user interaction BUT this is where it gets stuck when called by Disconnect() method (below). It awaits here forever
await _modeTask;
}
catch (TaskCanceledException ex)
{
Console.WriteLine(ex.Message);
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// After sucessful await create new cts
_cancellationToken.Dispose();
_cancellationToken = new CancellationTokenSource();
}
}
// Example of LED effect loop
internal async Task Static(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
_ledStrip.FillLedsWithColor();
// Wait for strip to light up
await LightLeds();
// Delay before next loop round
await Task.Delay(15, cancellationToken);
}
}
// This is being called by window onclosing
internal async Task Disconnect()
{
//Stop current task and close serial connection. _device is serial
await StopTask();
Application.Current.Dispatcher.Invoke(() =>
{
if (_device.IsOpen())
{
_device.Clear();
_device.Close();
}
});
}
// Method for sending LED information to Arduino
internal async Task LightLeds()
{
if (!_device.IsOpen())
return;
await Task.Run(() =>
{
for (int i = 0; i < StaticValues.NumLeds; i++)
{
_device.Send((byte)i, _ledStrip.Leds[i].LedColor.R, _ledStrip.Leds[i].LedColor.G, _ledStrip.Leds[i].LedColor.B);
}
_device.LightUp();
});
}
I am beginner with Tasks and I am pretty sure that I am not using them properly (some of them are certainly unnecessary but I don't know it) and maybe it's the reason why it's not working. I tried to search and found many examples for using Tasks but I still don't understand it well.
Thank you!
Change MainWindow_OnClosing() to be async void instead, and use await Disconnect() instead of calling .Wait(). Event handlers are almost the only async methods for which this acceptable; the rest should have an async Task[<T>] signature instead. (There are some exceptions to the async part, but not the Task part, but I won’t muddy the waters with that here). This will stop you blocking (see link in Dmytro’s comment for more).
While there, change CbMode_OnSelectionChanged() to be similar (async void), make ChangeMode() async Task, and await it instead.
The only other minor thing of note is that if you move the device-closing code into your event handler instead (or refactor it into another method that you call from the event handler, after await Disconnect()), you should not need the Invoke(), as async event handlers - done correctly - give you this for free; i.e. effectively remaining on the UI thread while not blocking. (I’m assuming that’s what you trying to achieve there?)
I am trying to stop multiple recursive async tasks fired by an initial Parallel.Foreach loop by using CancellationToken in my WinForms C# program, the program works nice without lagging the GUI but once I click the stop button the program becomes really laggish.
With small number of tasks it works okay-ish but when the number of concurrent tasks are high in number (more than 20 let's say) it doesn't stop some tasks properly or doesn't close some of them at all.
When this bug happens and I click the start button (button1) again where there is the main Parallel.Foreach loop, for some reason it creates a smaller number of tasks.
This way I am forced to close and reopen the program.
Is there a better way to stop multiple recursive async tasks?
This is my code and what I have tried so far:
private async void button1_Click(object sender, EventArgs e)
{
cts = new CancellationTokenSource();
await Task.Run(() => Parallel.ForEach(Array, async s =>
{
try
{
if (!cts.Token.IsCancellationRequested)
{
await LaunchMethod(cts.Token);
}
}
catch (System.OperationCanceledException)
{
Console.WriteLine("Aborting task");
}
}));
}
And this is the recursive method:
private async Task LaunchMethod(CancellationToken ct)
{
//Really long CPU and NETWORK intensive method here
//Throwing Cancellation Request periodically during the long method like this
ct.ThrowIfCancellationRequested();
//Then calling the recursive method and passing the token
try
{
if (counter < NumberOfLoops)
{
counter++;
await LaunchMethod(ct)
}
}
catch (System.OperationCanceledException)
{
Console.WriteLine("Aborting task");
}
}
And this is the stop button
private void CancelRequest()
{
if (cts != null)
{
cts.Cancel();
cts.Dispose();
}
}
I have a windows form (AlertForm) that displays a progress bar. I am trying to show it an close it thru a task. The problem that I am having is when I spawn a thread and call winforms.showdialog() it holds the thread and therefore cant cancel it. I am doing this because I am writing an excel add in thru c# in which I don't have a panel to show a progress bar.
static void Main(string[] args)
{
AlertForm alert = new AlertForm();
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
Task.Factory.StartNew(() =>
{
//while (true)
//{
// do some heavy work here
alert.ShowDialog();
Thread.Sleep(100);
if (ct.IsCancellationRequested)
{
alert.Close();
// another thread decided to cancel
Console.WriteLine("task canceled");
//break;
}
//}
}, ct);
// Simulate waiting 3s for the task to complete
Thread.Sleep(3000);
// Can't wait anymore => cancel this task
ts.Cancel();
Console.ReadLine();
}
How do I open the form and not hold it so at a later stage when a long task is complete I can cancel the task which will close the window
(AlertForm)?
If the AlertForm class is inheritance from Windows.Forms.Control then you can use Invoke method
alert.Invoke((MethodInvoker)delegate ()
{
alert.Close();
});
Example:
Once I display a messagebox, I'd like to call into a function to automatically exit the environment AFTER 5 seconds of the messagebox being displayed.
Is there a better approach towards doing so other than using a timer (starting the timer before the messagebox is displayed)?
Thanks
Using a timer is definitely a valid solution in this case, but you can also leverage Tasks and async/await in order to gain more control over the execution flow. Here's a Task-based implementation which calls Environment.Exit(0) after the predefined time interval, or immediately after the user dismisses the message box:
static void ShowMessageBoxAndExit(string text, TimeSpan exitAfter)
{
using (CancellationTokenSource cts = new CancellationTokenSource())
{
Func<Task> exitTaskFactory = async () =>
{
try
{
await Task.Delay(exitAfter, cts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Expected if the user dismisses the
// message box before the wait is completed.
}
finally
{
Environment.Exit(0);
}
};
// Start the task.
Task exitTask = exitTaskFactory();
MessageBox.Show(text);
// Cancel the wait if the user dismisses the
// message box before our delay time elapses.
cts.Cancel();
// We don't want the user to be able to perform any more UI work,
// so we'll deliberately block the current thread until our exitTask
// completes. This also propagates task exceptions (if any).
exitTask.GetAwaiter().GetResult();
}
}
I'm developing a C# operation and I would like to show a modal progress dialog, but only when an operation will be long (for example, more than 3 seconds). I execute my operations in a background thread.
The problem is that I don't know in advance whether the operation will be long or short.
Some software as IntelliJ has a timer aproach. If the operation takes more than x time, then show a dialog then.
What do you think that is a good pattern to implement this?
Wait the UI thread with a timer, and show dialog there?
Must I DoEvents() when I show the dialog?
Here's what I'd do:
1) Use a BackgroundWorker.
2) In before you call the method RunWorkerAsync, store the current time in a variable.
3) In the DoWork event, you'll need to call ReportProgress. In the ProgressChanged event, check to see if the time has elapsed greater than three seconds. If so, show dialog.
Here is a MSDN example for the BackgroundWorker: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
Note: In general, I agree with Ramhound's comment. Just always display the progress. But if you're not using BackgroundWorker, I would start using it. It'll make your life easier.
I will go with the first choice here with some modifications:
First run the possible long running operation in different thread.
Then run a different thread to check the first one status by a wait handle with timeout to wait it for finish. if the time out triggers there show the progress bar.
Something like:
private ManualResetEvent _finishLoadingNotifier = new ManualResetEvent(false);
private const int ShowProgressTimeOut = 1000 * 3;//3 seconds
private void YourLongOperation()
{
....
_finishLoadingNotifier.Set();//after finish your work
}
private void StartProgressIfNeededThread()
{
int result = WaitHandle.WaitAny(new WaitHandle[] { _finishLoadingNotifier }, ShowProgressTimeOut);
if (result > 1)
{
//show the progress bar.
}
}
Assuming you have a DoPossiblyLongOperation(), ShowProgressDialog() and HideProgressDialog() methods, you could use the TPL to do the heavy lifting for you:
var longOperation = new Task(DoPossiblyLongOperation).ContinueWith(() => myProgressDialog.Invoke(new Action(HideProgressDialog)));
if (Task.WaitAny(longOperation, new Task(() => Thread.Sleep(3000))) == 1)
ShowProgressDialog();
I would keep the progress dialog separate from the background activity, to separate my UI logic from the rest of the application. So the sequence would be (This is essentially the same as what IntelliJ does):
UI starts the background operation (in a BackgroundWorker) and set up a timer for X seconds
When the timer expires UI shows the progress dialog (if the background task is still running)
When the background task completes the timer is cancelled and the dialog (if any) is closed
Using a timer instead of a separate thread is more resource-efficient.
Recommended non-blocking solution and no new Threads:
try
{
var t = DoLongProcessAsync();
if (await Task.WhenAny(t, Task.Delay(1000)) != t) ShowProgress();
await t;
}
finally
{
HideProgress();
}
I got the idea from Jalal Said answer. I required the need to timeout or cancel the progress display. Instead of passing an additional parameter (cancellation token handle) to the WaitAny I changed the design to depend on Task.Delay()
private const int ShowProgressTimeOut = 750;//750 ms seconds
public static void Report(CancellationTokenSource cts)
{
Task.Run(async () =>
{
await Task.Delay(ShowProgressTimeOut);
if (!cts.IsCancellationRequested)
{
// Report progress
}
});
}
Use it like so;
private async Task YourLongOperation()
{
CancellationTokenSource cts = new CancellationTokenSource();
try
{
// Long running task on background thread
await Task.Run(() => {
Report(cts);
// Do work
cts.Cancel();
});
}
catch (Exception ex) { }
finally {cts.Cancel();}
}