Stop Multiple Tasks at once - c#

I am starting multiple tasks with a parallel foreach loop.
Now I want on click of a stop button all task to be stopped. How can I do this?
Here is my code:
tasks2 = new List<Task>();
Parallel.ForEach<RssToProcess>(RssFeeds, rssFeed =>
tasks2.Add(Task.Factory.StartNew(() =>
{
string urlss = rssFeed.RssUrl;
nourl += urlss + System.Environment.NewLine;
RssReader rs = new RssReader();
rs.FeedsourceLoaded += new EventHandler(rs_FeedsourceLoaded);
rs.ItemAdded += new EventHandler(rs_ItemAdded);
rs.AllItemAdded += new EventHandler(rs_AllItemAdded);
rs.RssReaderrssitemsCountgeta += new EventHandler(rs_RssReaderrssitemsCountgeta);
rs.RdfMode = true;
RssFeed f = rs.Retrieve(rssFeed.RssUrl);
})));

You should probably be passing a CancellationToken:
http://msdn.microsoft.com/en-us/library/dd537607.aspx
Create a CancellationToken:
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
And pass it to your tasks:
Task.Factory.StartNew(() =>
{
... Do Work ...
}, token);
When you want to cancel, call
tokenSource.Cancel();
However, note that task cancellation is cooperative.
Within your tasks, you will need to use
token.ThrowIfCancellationRequested(); // To Abort immediately.
... OR ...
if (token.IsCancellationRequested)
{
// Exit your task manually.
}

Related

Multiple tasks running same method

I have a grid with multiple SQL queries, which I want users to allow to execute when they double click on a row in the grid. I want a user to be able to run multiple queries at the same time.
I have a small Form with a progress bar and timer for each query. The Form is displayed when a query is run and shows complete after the query has finished.
Below is my code, it seems that the method returning the data is blocking. I haven't worked with sync/threads/tasks before
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationTokenHook hook = new CancellationTokenHook(cts);
CancellationToken token = cts.Token;
//this.waitFormActivator.ShowWaitForm(true, true);
//this.waitFormActivator.SetWaitFormObject(hook);
Exception exception = null;
try
{
progressControl.Start();
Task<Dataset> t = Test(command.CommandText, token, saveFileDialog1.FileName);
var result = await t;
progressControl.Stop();
}
catch (Exception ex)
{
//success = false;
exception = ex;
//return null;
}
}
I would like the queries to run independently of each other, however currently the next query is waiting for the previous one to finish.
This piece of code is suppose to answer to your question
List<Task> tasks = new List<Task>();
tasks.Add(new Task(() => { action1 }));
tasks.Add(new Task(() => { action2 }));
tasks.Add(new Task(() => { action3 }));
tasks.Add(new Task(() => { action4 }));
Task.WhenAll(tasks);
In your context it's suppose to be like that :
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationTokenHook hook = new CancellationTokenHook(cts);
CancellationToken token = cts.Token;
his.waitFormActivator.ShowWaitForm(true, true);
his.waitFormActivator.SetWaitFormObject(hook);
eption exception = null;
try
{
progressControl.Start();
List<Task<Dataset>> tasks = new List<Task<DataSert>>();
Task<Dataset> t1 = Test(command.CommandText, token, saveFileDialog1.FileName);
Task<Dataset> t2 = Test(command.CommandText, token, saveFileDialog1.FileName);
Task<Dataset> t3 = Test(command.CommandText, token, saveFileDialog1.FileName);
Task<Dataset> t4 = Test(command.CommandText, token, saveFileDialog1.FileName);
tasks.Add(t1);
tasks.Add(t2);
tasks.Add(t3);
tasks.Add(t4);
var result = Task.WhenAll(tasks);
progressControl.Stop();
}
catch (Exception ex)
{
//success = false;
exception = ex;
//return null;
}
}

get long process progress in async task function

I have a async function and used the Progress< int>
to progress long process After I run another task, it seems that the many progress reports are running.
private async void Listbox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var progress = new Progress<int>(percent =>
{
prg.Value = percent;
});
isCanceled = true;
await ExecuteManuallyCancellableTaskAsync(progress);
}
and this is my func
public async Task ExecuteManuallyCancellableTaskAsync(IProgress<int> progress)
{
var mprogress = 0;
prg.Value = 0;
using (var cancellationTokenSource = new CancellationTokenSource())
{
cancellationTokenSource.Cancel();
var SearchTask = Task.Run(async () =>
{
foreach (var file in await GetFileListAsync(GlobalData.Config.DataPath))
{
if (isCanceled)
{
cancellationTokenSource.Cancel();
return;
}
mprogress += 1;
progress.Report((mprogress * 100 / TotalItem));
await Dispatcher.InvokeAsync(() =>
{
// my codes
}, DispatcherPriority.Background);
}
});
await SearchTask;
}
}
and this is result that you can see different value in progressbar same time.
In short you are not using the CancellationTokenSource correctly for 2 reasons; firstly it needs to be passed around to any async methods that you are calling in order to truly cancel them, secondly it will likely need to live for longer than just inside the scope where you are using it.
Try something like this (complete with comments to hopefully make it easy to follow):
private CancellationTokenSource cancellationTokenSource; // And remove isCanceled as this is causing some of the issues
private async void Listbox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var progress = new Progress<int>(percent =>
{
prg.Value = percent;
});
// Make sure any current processing is stopped.
cancellationTokenSource?.Cancel();
// Prepare to be able to cancel the next round of processing.
cancellationTokenSource = new CancellationTokenSource();
await ExecuteManuallyCancellableTaskAsync(progress, cancellationTokenSource.Token);
}
public async Task ExecuteManuallyCancellableTaskAsync(IProgress<int> progress, CancellationToken cancelToken)
{
var mprogress = 0;
prg.Value = 0;
await Task.Run(async () =>
{
// You will need to implement checks against the CancellationToken in your GetFileListAsync method also.
foreach (var file in await GetFileListAsync(GlobalData.Config.DataPath, cancelToken))
{
mprogress += 1;
progress.Report((mprogress * 100 / TotalItem));
// Only update the UI if we have not been requested to cancel.
if (!cancelToken.IsCancellationRequested)
{
await Dispatcher.InvokeAsync(() =>
{
// my codes
}, DispatcherPriority.Background);
}
}
}, cancelToken); // Pass in the token to allow the Task to be cancelled.
}

How to do sthg when cancellation request returns true?

Below code starts counting and after 3 seconds, it will print the value of "i" variable.
The program prints the variable but it does not print in the style that I wanted. It does not print the Console.Writeline statement under if (token.IsCancellationRequested) part. I think the program directly exits when Cancellation request is true. Is it possible that I print out that console.writeline statement when cancellation request is true?
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication17
{
class Program
{
private static void Stop(CancellationTokenSource src)
{
Thread.Sleep(3000);
src.Cancel();
}
static void Count(CancellationToken token)
{
for (int i = 0; i < 100000; i++)
{
Console.WriteLine(i);
Thread.Sleep(80);
if (token.IsCancellationRequested)
{
Console.WriteLine("Current number is :" + i.ToString());
break;
}
else
{
Console.Clear();
}
}
}
static void Main(string[] args)
{
CancellationTokenSource src1 = new CancellationTokenSource();
CancellationToken tkn1 = new CancellationToken();
var task1 = Task.Run(() => Count(tkn1), tkn1);
var task2 = Task.Run(() => Stop(src1));
task2.Wait();
}
}
}
Your token is disconnected from CancellationTokenSource:
CancellationTokenSource src1 = new CancellationTokenSource();
CancellationToken tkn1 = new CancellationToken();
As you see - you just create new token without any relation to source. So when you request cancellation on token source - it cannot affect that token in any way. Instead of creating new token - use token from source:
CancellationToken tkn1 = src1.Token;
Note that there is also a race condition here:
var task1 = Task.Run(() => Count(tkn1), tkn1);
var task2 = Task.Run(() => Stop(src1));
task2.Wait();
You are waiting for task2 to complete, after that process immediately exits. task2 is task that request token cancellation. After token is cancelled it might take up to 80ms (as per your code) for your loop to notice that and write a message. Before this happens - process might already have exited and you will see no message. To avoid that - wait for task1 (actual loop) to complete instead:
var task1 = Task.Run(() => Count(tkn1), tkn1);
var task2 = Task.Run(() => Stop(src1));
task1.Wait();

when Task.IsCancelled is set as true?

when will come Task.IsCanceled = true;
Code:
var cts = new CancellationTokenSource();
string result = "";
cts.CancelAfter(10000);
try
{
Task t = Task.Run(() =>
{
using (var stream = new WebClient().OpenRead("http://www.rediffmail.com"))
{
result = "success!";
}
cts.Token.ThrowIfCancellationRequested();
}, cts.Token);
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.IsRunning)
{
if (timer.ElapsedMilliseconds <= 10000)
{
if (result != ""){
timer.Stop();
Console.WriteLine(result);
}
}
else
{
timer.Stop();
//cts.Cancel();
//cts.Token.ThrowIfCancellationRequested();
}
}
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled); // still its appear in false.
}
My requirement is - Task is not completed upto 10seconds, Need to cancel the task.
So I am setting timer and watch upto the given seconds. its not completed mean cancel the task and showing error message.
You have to pass the token to your method. It should inspect the token and respect the call to Cancel() of the CancellationTokenSource.
Or you do it yourself:
Task t = Task.Factory.StartNew(() =>
{
myResult = method(); // Request processing in parallel
cts.Token.ThrowIfCancellationRequested(); // React on cancellation
}, cts.Token);
A complete example is this:
async Task Main()
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
cts.CancelAfter(500);
Task t = null;
try
{
t = Task.Run(() => { Thread.Sleep(1000); ct.ThrowIfCancellationRequested(); }, ct);
await t;
Console.WriteLine(t.IsCanceled);
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled);
}
}
The output is that an OperationCanceledException is thrown and the result is
True
if you remove the ct.ThrowIfCancellationRequested(); part it will show
False
Edit:
Now, you have updated the question, some comments on that. First, you won't need the timer anymore since you are using the CancelAfter method. Second, you need to await your task. So that makes something like this:
string result = "";
cts.CancelAfter(10000);
Task t = null;
try
{
t = Task.Run(() =>
{
using (var stream = new WebClient().OpenRead("http://www.rediffmail.com"))
{
cts.Token.ThrowIfCancellationRequested();
result = "success!";
}
}, cts.Token);
await t;
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled);
}
This should show that t.IsCanceled is true but of course only when the call of the WebClient takes longer that 10 seconds.
From documentation:
A Task will complete in the TaskStatus.Canceled state under any of the following conditions:
Its CancellationToken was marked for cancellation before the task started executing.
The task acknowledged the cancellation request on its already signaled CancellationToken by throwing an OperationCanceledException that bears the same CancellationToken.
The task acknowledged the cancellation request on its already signaled CancellationToken by calling the ThrowIfCancellationRequested method on the CancellationToken.
So basically you would need to throw an OperationCanceledException within your task to force the state for instance by executing cts.Token.ThrowIfCancellationRequested() just after you cancel it.
But the intention of this mechanism is a bit the other way around. You cancel source say while user presses cancel button on your form (from outside of your task) an task just verifies if cancellation was requested in some safe to cancel points of its code.

Correct way to delay the start of a Task

I want to schedule a task to start in x ms and be able to cancel it before it starts (or just at the beginning of the task).
The first attempt would be something like
var _cancelationTokenSource = new CancellationTokenSource();
var token = _cancelationTokenSource.Token;
Task.Factory.StartNew(() =>
{
token.ThrowIfCancellationRequested();
Thread.Sleep(100);
token.ThrowIfCancellationRequested();
}).ContinueWith(t =>
{
token.ThrowIfCancellationRequested();
DoWork();
token.ThrowIfCancellationRequested();
}, token);
But I feel like there should be a better way, as this would use up a thread while in the sleep, during which it could be canceled.
What are my other options?
Like Damien_The_Unbeliever mentioned, the Async CTP includes Task.Delay. Fortunately, we have Reflector:
public static class TaskEx
{
static readonly Task _sPreCompletedTask = GetCompletedTask();
static readonly Task _sPreCanceledTask = GetPreCanceledTask();
public static Task Delay(int dueTimeMs, CancellationToken cancellationToken)
{
if (dueTimeMs < -1)
throw new ArgumentOutOfRangeException("dueTimeMs", "Invalid due time");
if (cancellationToken.IsCancellationRequested)
return _sPreCanceledTask;
if (dueTimeMs == 0)
return _sPreCompletedTask;
var tcs = new TaskCompletionSource<object>();
var ctr = new CancellationTokenRegistration();
var timer = new Timer(delegate(object self)
{
ctr.Dispose();
((Timer)self).Dispose();
tcs.TrySetResult(null);
});
if (cancellationToken.CanBeCanceled)
ctr = cancellationToken.Register(delegate
{
timer.Dispose();
tcs.TrySetCanceled();
});
timer.Change(dueTimeMs, -1);
return tcs.Task;
}
private static Task GetPreCanceledTask()
{
var source = new TaskCompletionSource<object>();
source.TrySetCanceled();
return source.Task;
}
private static Task GetCompletedTask()
{
var source = new TaskCompletionSource<object>();
source.TrySetResult(null);
return source.Task;
}
}
Since .NET 4.5 has now been released, there's a very simple built-in way to delay a task: just use Task.Delay(). behind the scenes, it uses the implementation that ohadsc decompiled.
The correct answer in the future will probably be Task.Delay. However, that's currently only available through the Async CTP (and in the CTP, it's on TaskEx rather than Task).
Unfortunately, because it's only in CTP, there aren't many good links to documentation for it either.
Look at the TaskFactoryExtensions_Delayed in "Parallel Programming with .NET 4 Samples".
I haven't tested this, but here is a first-pass at wrapper methods to create an initial 'Delay' Task or to continue after a Delay. If you find issues, feel free to correct.
public static Task StartDelayTask(int delay, CancellationToken token)
{
var source = new TaskCompletionSource<Object>();
Timer timer = null;
timer = new Timer(s =>
{
source.TrySetResult(null);
timer.Dispose();
}, null, delay, -1);
token.Register(() => source.TrySetCanceled());
return source.Task;
}
public static Task ContinueAfterDelay
(this Task task,
int delay, Action<Task> continuation,
CancellationToken token)
{
var source = new TaskCompletionSource<Object>();
Timer timer = null;
var startTimer = new Action<Task>(t =>
{
timer = new Timer(s =>
{
source.TrySetResult(null);
timer.Dispose();
},null,delay,-1);
});
task.ContinueWith
(startTimer,
token,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Current);
token.Register(() => source.TrySetCanceled());
return source.Task.ContinueWith(continuation, token);
}
You can use Token.WaitHandle.WaitOne(int32 milliseconds) overload method to specify number of milliseconds to wait for your task. But key difference between Thread.Sleep(xxx) and Token.WaitHandle.WaitOne(xxx) that later blocks thread until the time specified elapsed or the token has been canceled.
Here is an example
void Main()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var task = Task.Factory.StartNew(() =>
{
// wait for 5 seconds or user hit Enter key cancel the task
token.WaitHandle.WaitOne(5000);
token.ThrowIfCancellationRequested();
Console.WriteLine("Task started its work");
});
Console.WriteLine("Press 'Enter' key to cancel your task");
Console.Read();
tokenSource.Cancel();
}

Categories