how to to request the Task to stop working? - c#

I'm using such code:
private Task[] tasks;
private volatile bool stayConnected;
....
tasks[i] = Task.Factory.StartNew(() =>
{
while (stayConnected) {
...
}
}
....
public void Disconnect()
{
stayConnected = false;
Task.WaitAll(tasks);
}
My code doesn't work - tasks never ends. I guess they do not receive fresh stayConnected = false value by some reason, probably just declaring field as volatile is not enough.
What is the correct way to signal a task to stop working?
I don't want to "kill" the task, instead i want to "request" the task to finish.
I've found this article that suggests to use CancellationToken, should I use it or probably you can suggest something better? Can I use one CancellationToken for several tasks?
Also it's interesting why my initial code doesn't work.

It is better to use CancellationToken Structure but Boolean value.
CancellationTokenSource cts;
private Task[] tasks;
void Start()
{
cts = new CancellationTokenSource();
tasks = new Task[1];
tasks [0] = Task.Run(() => SomeWork(cts.Token), cts.Token);
}
void SomeWork(CancellationToken cancellationToken)
{
while (true)
{
// some work
cancellationToken.ThrowIfCancellationRequested();
}
}
void Cancel()
{
cts.Cancel();
Task.WaitAll(tasks);
}

Related

System.Threading.Timer stuck for some time when server has load

We have below class members for timer:
private Timer _activityTimer;
Instantiating this timer variable in one method:
_activityTimer =
new Timer(async (timerState) => await UpdateActivityAsync(_ipAddress), null, new Random().Next(1, 15000), 15000);
But this did not calling periodically when server has load.
It is showing below log in serilog:
Starting HttpMessageHandler cleanup cycle with {InitialCount} items
Ending HttpMessageHandler cleanup cycle after {ElapsedMilliseconds}ms - processed: {DisposedCount} items - remaining: {RemainingItems} items
To handle async periodic callback, I would use System.Threading.PeriodicTimer. This way the execution of the next UpdateActivityAsync will not begin until the last one is done. If you still face thread pool starvation issue, you could manually create an additional thread in which you run the timer.
class Example {
private PeriodicTimer _activityTimer;
private IPAddress _ipAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void StopTimer() {
_activityTimer.Dispose();
}
private async Task StartTimerLoopAsync() {
_activityTimer = new PeriodicTimer(TimeSpan.FromMilliseconds(15000));
while (await _activityTimer.WaitForNextTickAsync()) {
await UpdateActivityAsync(_ipAddress);
}
}
private async Task UpdateActivityAsync(IPAddress ipAddress) {
await Task.Delay(500); // Simulate some IO
Console.WriteLine(ipAddress);
}
}
UPDATE for .NET Core 3.1
Instead of PeriodicTimer you could simply use Task.Delay (it's not very accurate, but good enough for your use case I believe):
class Example {
private CancellationTokenSource _cancellationTokenSource = new ();
private IPAddress _ipAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void StopTimer() {
_cancellationTokenSource.Cancel();
}
private async Task StartTimerLoopAsync() {
await Task.Delay(new Random().Next(1, 15000)); // System.Threading.Timer first delay
while (!_cancellationTokenSource.IsCancellationRequested) {
// run the Delay and UpdateActivityAsync simultaneously and wait for both
var delayTask = Task.Delay(15000, _cancellationTokenSource.Token);
await UpdateActivityAsync(_ipAddress, _cancellationTokenSource.Token);
await delayTask;
}
}
private async Task UpdateActivityAsync(IPAddress ipAddress, CancellationToken cancellationToken) {
await Task.Delay(1500, cancellationToken); // Simulate some IO
Console.WriteLine(ipAddress);
}
}
UPDATE
You could also create your own async Timer:
class Example : IDisposable {
private AsyncTimer<Example>? _timer;
public IPAddress IpAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
if (_timer is not null) {
return;
}
_timer = new AsyncTimer<Example>(async (state, ct) => await UpdateActivityAsync(state.IpAddress, ct), this, new Random().Next(1, 15000), 15000);
}
public void StopTimer() {
_timer?.Stop();
}
private async Task UpdateActivityAsync(IPAddress ipAddress, CancellationToken cancellationToken) {
await Task.Delay(500, cancellationToken); // Simulate some IO
Console.WriteLine(ipAddress);
}
public void Dispose() {
_timer?.Dispose();
}
}
class AsyncTimer<T> : IDisposable {
public delegate Task AsyncTimerDelegate(T state, CancellationToken cancellationToken);
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly AsyncTimerDelegate _timerCallback;
private readonly TimeSpan _dueTime;
private readonly TimeSpan _interval;
private readonly T _state;
public AsyncTimer(AsyncTimerDelegate timerCallback, T state, int dueTime, int interval) {
_timerCallback = timerCallback;
_state = state;
_dueTime = TimeSpan.FromMilliseconds(dueTime);
_interval = TimeSpan.FromMilliseconds(interval);
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void Stop() {
_cancellationTokenSource.Cancel();
}
private async Task StartTimerLoopAsync() {
await Task.Delay(_dueTime);
while (!_cancellationTokenSource.IsCancellationRequested) {
// run the Delay and UpdateActivityAsync simultaneously and wait for both
var delayTask = Task.Delay(_interval, _cancellationTokenSource.Token);
await _timerCallback.Invoke(_state, _cancellationTokenSource.Token);
await delayTask;
}
}
public void Dispose() {
_cancellationTokenSource.Dispose();
}
}

Return a Task from a method with type void

I would like to create a task to run serial commands on. At this time I do not need to return anything from the method that is doing the work. This will probably change later, but I am now curious as to how this.
This is what I have. I would like to use a separate method for the task instead of creating an anonymous action. I have tried returning void, with the result of "void can not be explicitly converted to a Task". I have also tried. Task<void>. The Last thing I have tried is returning a Task, but I receive, error "Not all Code paths return a value" and "Can not implicily convert void to type task"
In the pass I have used a Thread to accomplish this, but I'd like to use Tasks this time around.
internal class Hardware
{
private EventHandler<SequenceDoneEventArgs> SequenceDone;
private List<Step> Steps;
private System.IO.Ports.SerialPort comport = null;
private Task SequenceTask;
private CancellationTokenSource RequestStopSource;
private CancellationToken RequestStopToken;
private void Initialize()
{
comport = new System.IO.Ports.SerialPort("COM2", 115200, System.IO.Ports.Parity.None,8);
comport.DataReceived += Comport_DataReceived;
}
public async void RunSequence()
{
if (comport == null)
{
Initialize();
}
if (!comport.IsOpen)
{
comport.Open();
}
RequestStopSource = new CancellationTokenSource();
RequestStopToken = RequestStopSource.Token;
SequenceTask = await Task.Run(() => { doSequence(); });
}
private Task doSequence()
{
//** Run Sequence stuff here
}
}
ETA:
In the end this is my the complete solution
internal class Hardware
{
private EventHandler<SequenceDoneEventArgs> SequenceDone;
private List<Step> Steps;
private System.IO.Ports.SerialPort comport = null;
private Task SequenceTask;
private CancellationTokenSource RequestStopSource;
private CancellationToken RequestStopToken;
private void Initialize()
{
comport = new System.IO.Ports.SerialPort("COM2", 115200, System.IO.Ports.Parity.None,8);
comport.DataReceived += Comport_DataReceived;
}
public async void RunSequence()
{
if (comport == null)
{
Initialize();
}
if (!comport.IsOpen)
{
comport.Open();
}
RequestStopSource = new CancellationTokenSource();
RequestStopToken = RequestStopSource.Token;
SequenceTask = await Task.Factory.StartNew(async () => { await doSequence(); });
}
private Task doSequence()
{
//** Run Sequence stuff here
//return null;
return Task.CompletedTask;
}
}
Just mark doSequence as async (assuming it uses await):
private async Task doSequence()
Also, it's a good idea to return this Task in the delegate you pass to Task.Run:
SequenceTask = await Task.Run(() => doSequence());
I would like to create a task to run serial commands on.
This leads me to believe that using async and Task may not be the best solution for your scenario. I suggest you look into TPL Dataflow.
SequenceTask = await Task.Factory.StartNew(async() => { await doSequence(); });
Also your RunSequence() should return Task instead of void.
Actually if you await the Task this should result in the same:
SequenceTask = await doSequence();

Cancelling execution of a method called with await

I'm trying to cancel the execution of the DoSomethingAsync method which I call using await.
When I click on the cancel button the execution is not cancelled and I don't see the "Execution was cancelled" message box, but instead I see the other message box.
I don't understand why it's not working. I am still learning this part of C# and I took this example at http://www.codeproject.com/Articles/127291/C-vNext-New-Asynchronous-Pattern#heading0015 (I simplified it).
public class MyClass : Class
{
CancellationTokenSource cts;
private async void searchButton_Click(object sender, EventArgs e)
{
await DoSomethingAsync();
}
private void cancelButton_Click(object sender, EventArgs e)
{
cts.Cancel();
}
async void DoSomethingAsync()
{
cts = new CancellationTokenSource();
try
{
await SuperSlowProcess();
MessageBox.Show("You will only see this if execution is not cancelled");
}
catch (TaskCanceledException)
{
MessageBox.Show("Execution was cancelled");
}
}
}
In order to make it working, you actually need to use CancellationToken in the SuperSlowProcess:
public Task SuperSlowProcess(CancellationToken cancellationToken)
{
return Task.Run(() => {
// you need to check cancellationToken periodically to check if cancellation has been requested
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested(); // this will throw OperationCancelledException after CancellationTokenSource.Cancel() is called
Thread.Sleep(200); // to emulate super slow process
}
});
}
Of course, it depends on the implementation of SuperSlowProcess. If it's not possible to check CancellationToken periodically, you could check it only once - in the very end, something like that:
public async Task SuperSlowProcess2(CancellationToken cancellationToken)
{
var response = await CallExternalApi();
cancellationToken.ThrowIfCancellationRequested();
}
and then
async void DoSomethingAsync()
{
cts = new CancellationTokenSource();
try
{
await SuperSlowProcess(cts.Token);
MessageBox.Show("You will only see this if execution is not cancelled");
}
catch (OperationCanceledException) // Note that exception type is different
{
MessageBox.Show("Execution was cancelled");
}
}

Best approach for cancel an awaitable method before navigation?

I am working in Xamarin app, but I think my question is more focused in .NET framework and C#.
For example I navigate to PageOne, and in constructor is called the asynchronous method InitializePageOneData()...
public PageOne()
{
await InitializePageOneData()
}
But just in this moment, while it is waiting for the method execution, I navigate to a second page (PageTwo), which has another asynchronous operations, but I see that the InitializePageOneData() method does not stops his execution.
My goal is to stop this asynchronous operation before doing the navigation to another page. What is your recommendation?
Note: in the asynchronous operation, I am using TaskCompletionSource:
private Task<Response> ProcessRequest(Request request)
{
tsc = new TaskCompletionSource<Response>();
eventHandler = (s, e) =>
{
try
{
_client.ProcessRequestsCompleted -= eventHandler;
tsc.TrySetResult(e.Result);
}
catch (Exception ex)
{
_client.ProcessRequestsCompleted -= eventHandler;
tsc.TrySetException(ex);
}
};
_client.ProcessRequestsCompleted += eventHandler;
_client.ProcessRequestsAsync(request);
return tsc.Task;
}
My goal is to stop this asynchronous operation before doing the
navigation to another page. What is your recommendation?
Pass down a CancellationToken to your async method, and monitor on that token. If the user wants to navigate away to page two, use the CancellationTokenSource to cancel that operation.
An example would be:
private CancellationTokenSource cts = new CancellationTokenSource();
public async Task LoadPageOneAsync()
{
try
{
await InitializePageOneDataAsync(cts.Token)
}
catch (OperationCanceledException e)
{
// Handle if needed.
}
}
public async Task InitializePageOneDataAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
foreach (var something in collection)
{
cancellationToken.ThrowIfCancellationRequested();
// Do other stuff
}
}
When you want to cancel, invoke cts.Cancel() from PageTwo:
public async Task LoadPageTwoAsync()
{
cts.Cancel();
cts = new CancellationTokenSource();
await LoadSecondPageAsync();
}

Canceling Task Delay in .Net 4.0

I am currently trying to implement a substitute for .Net 4.5's Task.Delay() method in a program that must target .Net 4.0. I found the following code at this blog.
/* You can write Task-based asynchronous methods by utilizing a TaskCompletionSource.
A TaskCompletionSource gives you a 'slave' Task that you can manually signal.
Calling SetResult() signals the task as complete, and any continuations kick off. */
void Main()
{
for (int i = 0; i < 10000; i++)
{
Task task = Delay (2000);
task.ContinueWith (_ => "Done".Dump());
}
}
Task Delay (int milliseconds) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
Tasks are fairly new to me. System.Threading.Timer and TaskCompletionSource are brand new to me (as of today), and I'm struggling a bit with them. All that aside, I'm wondering how I might add CancellationToken functionality to this code. I'm assuming I could add a parameter to the Delay() method like this:
Task Delay (int milliseconds, CancellationToken token) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
... but then, where do I put the logic for checking the token and getting out of the method? Somewhere in the callback? Is this even possible?
I've tried to change your code as little as possible but here is a working example that behaves in the same way as Task.Delay.
It's important to note that I use TrySetCanceled and TrySetResult because the Timer could finish after the task is canceled. Ideally you want to stop the timer.
Also note a canceled task will throw a TaskCanceledException
static void Main(string[] args)
{
// A cancellation source that will cancel itself after 1 second
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));
try
{
// This will only wait 1 second because as it will be cancelled.
Task t = Delay(5000, cancellationTokenSource.Token);
t.Wait();
Console.WriteLine("The task completed");
}
catch (AggregateException exception)
{
// Expecting a TaskCanceledException
foreach (Exception ex in exception.InnerExceptions)
Console.WriteLine("Exception: {0}", ex.Message);
}
Console.WriteLine("Done");
Console.ReadLine();
}
private static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
token.Register(() => tcs.TrySetCanceled());
Timer timer = new Timer(_ => tcs.TrySetResult(null));
timer.Change(milliseconds, -1);
return tcs.Task;
}
Reading a bit more into your question. If you need Task.Delay and you're targeting .NET 4.0 then you should use the Microsoft Async nuget package from http://www.nuget.org/packages/Microsoft.Bcl.Async/ it contains the method TaskEx.Delay
Like this:
token.Register(() => tcs.TrySetCancelled());
Here you are a version that prevents disposal of timer by the garbage collector
public static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
var timer = new OneShotTimer((t) => {
using ((OneShotTimer)t)
tcs.SetResult(null);
});
token.Register(() => {
if (timer.TryCancel())
{
using (timer)
tcs.SetCanceled();
}
});
timer.Start(milliseconds);
return tcs.Task;
}
public class OneShotTimer : IDisposable
{
private readonly object sync = new object();
private readonly TimerCallback oneShotCallback;
private readonly Timer timer;
private bool isActive;
public OneShotTimer(TimerCallback oneShotCallback, int dueTime = Timeout.Infinite)
{
this.oneShotCallback = oneShotCallback;
this.isActive = dueTime != Timeout.Infinite;
this.timer = new Timer(callback, this, dueTime, Timeout.Infinite);
}
public void Dispose()
{
timer.Dispose();
}
public void Start(int dueTime)
{
if (!tryChange(true, dueTime))
throw new InvalidOperationException("The timer has already been started");
}
public bool TryCancel()
{
return tryChange(false, Timeout.Infinite);
}
public bool tryChange(bool targetIsActive, int dueTime)
{
bool result = false;
lock (sync)
{
if (isActive != targetIsActive)
{
result = true;
isActive = targetIsActive;
timer.Change(dueTime, Timeout.Infinite);
}
}
return result;
}
private static void callback(object state)
{
var oneShotTimer = (OneShotTimer)state;
if (oneShotTimer.TryCancel())
oneShotTimer.oneShotCallback(oneShotTimer);
}
}

Categories

Resources