Cancel task and wait for it to finish - c#

I have a time consuming task which I need to run in a separate thread to avoid locking the GUI thread. As this task progresses, it updates a specific GUI control.
The catch is that the user might move to another part of the GUI before the task is over, and in that case, I have to:
Cancel the ongoing task (if it is active)
Wait till it's done cancelling: this is crucial, because the time consuming task's objective is to update a specific control. If more than one thread tries to do it at once, things might get messy.
Launch the task from scratch
For a concrete example, imagine the form has two parts: one where you navigate a directory tree, and another where you display thumbnails. When the user navigates to another directory, thumbnails need to be refreshed.
First I thought of using a BackgroundWorker and an AutoResetEvent to wait for cancellation, but I must have messed something because I got deadlocked when cancelling. Then I read about TPL, which is supposed to replace BGW and more primitive mechanisms.
Can this be done easily using TPL?

A few things to note:
You can get a CancellationToken from a CancellationTokenSource
Task cancellation is a cooperative action: if your task does not periodically check the CancellationToken.IsCancellationRequested property, it doesn't matter how many times you try to cancel the task, it will merrily churn away.
Those things said, here's the general idea:
void Main()
{
var tokenSource = new CancellationTokenSource();
var myTask = Task.Factory
.StartNew(() => DoWork(tokenSource.Token), tokenSource.Token);
Thread.Sleep(1000);
// ok, let's cancel it (well, let's "request it be cancelled")
tokenSource.Cancel();
// wait for the task to "finish"
myTask.Wait();
}
public void DoWork(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
// Do useful stuff here
Console.WriteLine("Working!");
Thread.Sleep(100);
}
}

Related

How to close all tasks and processes correctly when window is closed?

I am creating application with feature that open new window inside my application and starting additional processes and tasks inside them. I am realized that when we trying open and close window to often (less than one second per reopening) some tasks wasn't canceled and some processes as I wanted.
For canceling tasks and processes I use CancellationTokenSource, CancellationToken which one I Cancel() and Dispose() in Window.Closed [Event].
But if I will reopening my window too often its say me that Task was canceled or disposed.
So, How I can start my Tasks in Window constructor and closed correctly them when window closing?
Code:
MyAdditionalWindowClass
{
private CancellationTokenSource source;
private CancellationToken token;
private Process process;
MyAdditionalWindowClass()
{
source = new CancellationTokenSource();
token = source.Token;
Start();
thiswindow.Closed += (sender, e) => { process?.Kill(); process?.Close(); source.Cancel(); source.Dispose(); };
}
private async void Start()
{
await Task.Run(ToDoSomething), token);
//and other code after await
}
private void ToDoSomething()
{
//creating process, start him, do something
}
}
Your application should ask all components to close nicely when the user exits an application. Usually done by calling the Dispose method on any components that is disposable.
It the component has some background task it should tell this task to exit when it is disposed, for example by using a cancellation token, but there are other methods, like completing a blocking queue. Ideally you should use IAsyncDispose since that lets your dispose method return the task, so the caller can await it. If that is not available you need to decide if you can safely wait for the task, or if it is better to return before you are certain the background task has completed.
If the background task involves any IO you should wait for it to complete, otherwise you risk aborting in the middle of a write. If there is no IO involved, waiting is less important since the OS will cleanup the memory anyway.

My thread disappears from thread list after some time

I have multi thread program with C# language.
When load main form, 4 thread start that all of them work with while(true)
Every thread run, active and exist in thread list but after 30 ms, one of thread (without any error) disappear from thread list and doesn't work
even breakpoint not work.
I want read data from TCP and process 3 steps then save to database
Task1: have while(true) and read data from tcp and add to blockingcollection bk1
Task 2: in while(true), take data from bk1 and process data then add data to BlockingCollection bk2
Task3 in while(true) take data from bk2 and process then bk3
Task 4 in while (true) take data from bk3 then insert database
I define task:
Task.Factory.StartNew(() => myfunction, CancellationToken.None,
TaskCreationOptions.PreferFairness, TaskScheduler.Default);
When click button1 all of tasks start and work correctly after some time task3 change status to RanToCompletion and does not work I didn't use async and await in code because task works parallel and don't need wait for other task.
Even TaskCreationOptions set to LongRunning
My function have while(true) and work producer-consumer method.
Please help me about problem.
Thanks for attention.
Your code never awaits that task to end. Use await and Task.Run instead:
await Task.Run(()=>myFunction());
or
await Task.Run(myFunction);
Tasks aren't threads. They're a job that gets executed by a threadpoool thread. Creating a thread is an expensive operation. To avoid the cost of creating and destroying threads, .NET keeps a pool of worker threads. Jobs, in the form of Action or Func delegates, get posted to that ThreadPool and executed by one of the worker threads.
Task.Run or Task.Factor.StartNew post a job to the threadpool for execution and return a Task, essentially a "promise" object. This means the calling thread isn't blocked waiting for the task to complete and can do other work. await makes it easy to await for that task to complete and get back to the calling thread. Once the task completes, execution can resume with the code after await.
In a desktop application that means the UI thread doesn't get blocked waiting for a task to complete and can keep processing Windows messages, button clicks, refresh its windows etc. When the task completes, execution will resume on the UI thread with the code that comes after await.
async void btn1_Click(object sender,EventArgs args)
{
var customerName=txtCustomer.Text;
var orders=await Task.Run(()=>LoadOrdersFromDbAsync(customerName));
grdOrders.DataSource=orders;
}

Cancel a Task if it hasnt completed in 5 seconds [duplicate]

I am using an external library that has async methods, but not CancellationToken overloads.
Now currently I am using an extension method from another StackOverflow question to add a CancellationToken:
public async static Task HandleCancellation(this Task asyncTask, CancellationToken cancellationToken)
{
// Create another task that completes as soon as cancellation is requested. http://stackoverflow.com/a/18672893/1149773
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(() =>
tcs.TrySetCanceled(), useSynchronizationContext: false);
Task cancellationTask = tcs.Task;
// Create a task that completes when either the async operation completes, or
// cancellation is requested.
Task readyTask = await Task.WhenAny(asyncTask, cancellationTask);
// In case of cancellation, register a continuation to observe any unhandled exceptions
// from the asynchronous operation (once it completes). In .NET 4.0, unobserved task
// exceptions would terminate the process.
if (readyTask == cancellationTask)
asyncTask.ContinueWith(_ => asyncTask.Exception,
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
await readyTask;
}
However the underlying task still executes to completion. This wouldn't be much of a problem, but sometimes the underlying task never completes and consumes 99% of my CPU.
Is there any way to "kill" the task without killing the process?
I am using an extension method from another StackOverflow question
That code is very old.
The modern AsyncEx approach is an extension method Task.WaitAsync, which looks like this:
var ct = new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token;
await myTask.WaitAsync(ct);
I like how the API ended up because it's more clear that it's the wait that is cancelled, not the operation itself.
Is there any way to "kill" the task without killing the process?
No.
The ideal solution is to contact the authors of the library you're using and have them add support for CancellationToken.
Other than that, you're in the "cancel an uncancelable operation" scenario, which can be solved by:
Putting the code in a separate process, and terminating that process on cancellation. This is the only fully safe but most difficult solution.
Putting the code in a separate app domain, and unloading that app domain on cancellation. This is not fully safe; terminated app domains can cause process-level resource leaks.
Putting the code in a separate thread, and terminating that thread on cancellation. This is even less safe; terminated threads can corrupt program memory.
As you suggest you can cancel a task by passing in a CancellationToken and then calling Cancel.
As for how you'd go about triggering that cancellation depends on the nature of your application.
A few possible scenarios
Carry on until you click cancel
Cancel after a fixed time
Cancel if there's been no progress for a fixed time
In case 1 you simply cancel the task from your cancel button, for example
private void cancel_Click(object sender, RoutedEventArgs e)
{
...
cts = new CancellationTokenSource();
await MyAsyncTask(cts.Token);
cts.Cancel();
...
}
In case 2 you could start a timer when you start your task and then cancel the task after a set time using CancelAfter, for example
private void start_Click(object sender, RoutedEventArgs e)
{
...
cts = new CancellationTokenSource();
cts.CancelAfter(30000);
await MyAsyncTask(cts.Token);
...
}
In case 3 you could do something with progress, for example
private void start_Click(object sender, RoutedEventArgs e)
{
...
Progress<int> progressIndicator = new Progress<int>(ReportProgress);
cts = new CancellationTokenSource();
await MyAsyncTask(progressIndicator, cts.Token);
...
}
void ReportProgress(int value)
{
// Cancel if no progress
}
Here are a few useful links
Parallel programming, task cancellation, progress and cancellation, cancel tasks after set time, and cancel a list of tasks.
The only way I can think of is to change the TaskScheduler and mange the creation of the threads that are used for the tasks yourself. That is a lot of work.
The basic concept is to create your own implementation of the TaskScheduler, start a new task with your own scheduler assigned. This way you get your scheduler to be the current one and start your problematic task from this task.
There are still reason that may not work. If the task causing you trouble creates more tasks using the default task scheduler you still got the same problem. (Task.Run does so)
How ever if they are using the async/await key words your scheduler will remain active.
Now with the scheduler under your own control, you can kill any task by using Thread.Abort.
To get a idea about the implementation afford, you should have a look at the ThreadPoolTaskScheduler. That is the default implementation of the scheduler.
As I said this is a lot of work, but the only way I can think of to kill task that can't be cancelled.
To get a test running if that even works at all you may only want to implement the behaviour the ThreadPoolTaskScheduler has for the TaskCreationOptions.LongRunning option. So spawning a new thread for each task.

Get thread id and terminate thread

I have an async method that can run multiple times in the same time. This method perform a call to a server so it takes some time to complete. I want each time an instance of this method is called to stop the execution of the same method that run in the same time. I was thinking about creating a list of thread id and everytime the method is called the threads that created for the execution of the older instances of this method will be terminated.
So what i am looking for is a way to get the current thread id that a specific method is running from, and the way to terminate a thread using its id.
PS. I've checked all posts about finding thread id etc but none of these work in UWP.
In UWP you don't have ability to control threads. But you can surely control tasks, and for example, cancel it if needed with with CancellationToken:
CancellationTokenSource tokenSource = new CancellationTokenSource();
await Task.Run(() =>
{
// your job here
}, tokenSource.Token);
// then if you want to cancel the job use tokenSource:
tokenSource.Cancel();
Note that this is only a simple example of using CancellationToken with Task.Run, but most of async methods uses these tokens and even you can convert them to task which will use one.
More about task cancellation you will find at MSDN, Stephen Cleary's blog and more.

How to stop all tasks when one is finished in c#

I have different tasks to read from different files and find a word into them. I have put them into a task array which I start with waitAny method as following :
foreach (string file in filesList)
{
files[i] = Task.Factory.StartNew(() =>
{
mySearch.Invoke(file);
});
i++;
}
System.Threading.Tasks.Task.WaitAny(files);
I would like to stop all other tasks as soon as one of the tasks finishes (it finishes when it founds the word). For the moment, with waitany, i can know when one tasks finishes, but I don't know how I could know which one has finished and how to stop other tasks.
What would be the best way to achieve this ?
You can use single CancellationToken which all tasks will share. Inside mySearch.Invoke method verify value of token.IsCancellationRequested to cancel task. When some task will be finished cancel others via CancellationTokenSource.Cancel().
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
foreach (string file in filesList)
{
// pass cancellation token to your task
files[i] = Task.Factory.StartNew(() => mySearch.Invoke(file, token), token);
i++;
}
Task.WaitAny(files);
tokenSource.Cancel();
BTW you can force token to throw OperationCanceledException when source is canceled by calling token.ThrowIfCancellationRequested()
When creating a Task you can pass a CancelationToken. Set this token when one of the tasks finishes.
This will cause remaining tasks with this token to not execute. Running tasks can receive a OperationCanceledException and stop too.
I highly suggest reading How do I cancel non-cancelable async operations? by Stephen Toub. Essentially what you need to do is cancel all of these tasks, but currently you have no mechanism to cancel them.
The ideal approach would be to create a CancellationTokenSource before the foreach, pass the CancellationToken from that source to each of the child tasks, check that token periodically and stop doing work when you notice it's indicated cancellation. You can then cancel the token source in the WhenAny continuation.
If that's not an option you need to decide if it's important to actually stop the tasks (which, really, just can't be done) or if you just need to continue on with your code without waiting for them to finish (that's easy enough to do).

Categories

Resources