The idea: create a Task that prints an increasing number of asterisks. When the user presses Enter, the Task prints 10 asterisks and then stops.
The code:
namespace CancellingLongRunningTasks
{
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
var cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
Task task = Task.Run(() =>
{
int count = 1;
while (!token.IsCancellationRequested)
{
Console.WriteLine(new string('*', count));
Thread.Sleep(1000);
count++;
}
}, token).ContinueWith(
parent =>
{
var count = 10;
while (count > 0)
{
Console.WriteLine(new string('*', count));
Thread.Sleep(1000);
count--;
}
}, TaskContinuationOptions.OnlyOnCanceled);
Console.WriteLine("Press enter to stop the task.");
if (Console.ReadLine().Contains(Environment.NewLine))
{
cancellationTokenSource.Cancel();
task.Wait();
}
}
}
}
The question: why isn't my continuation task executed?
It isn't being executed because you're not actively cancelling the Task, only checking if a cancellation was requested. Use CancellationToken.ThrowIfCancellationRequested which throws an OperationCanceledException and will transform the Task into a cancelled state or simply throw the exception with the corresponding token:
Task task = Task.Run(() =>
{
int count = 1;
while (!token.IsCancellationRequested)
{
Console.WriteLine(new string('*', count));
Thread.Sleep(1000);
count++;
}
token.ThrowIfCancellationRequested();
}, token)
Which is equivalent to:
Task task = Task.Run(() =>
{
int count = 1;
while (!token.IsCancellationRequested)
{
Console.WriteLine(new string('*', count));
Thread.Sleep(1000);
count++;
}
throw new OperationCanceledException(token);
}, token)
Related
I wrote a command line program that lets two robots (Lifeguard and Superhero) wander randomly in a 22x22 matrix until they get to a square with the # symbol.
The direction choice of the robot Superhero is in contrast to the robot Lifeguard not equally distributed. Superhero chooses the direction north 66% of the time.
I can start both robots asynchronously via the menu. While the data for Lifeguard is available relatively fast, the program freezes because of Superhero. I assumed that it does not freeze because I am programming asynchronously. Where is the error?
using System.Diagnostics;
class Program {
public static async Task Main(string[] args) {
Map map = new Map();
Roboter l = new Roboter(map.defaultMap, "Lifesaver", 'L', 9);
Roboter s = new Roboter(map.defaultMap, "Superhero", 'S', 10);
map.spreadRoboters(l, s);
View view = new View(map.defaultMap);
Stopwatch watch = new Stopwatch();
Random rand = new Random();
string input = "start";
while (input != "exit") {
Console.WriteLine("choose an option:");
Console.WriteLine("1 start asynchronously");
Console.WriteLine("2 show Map");
Console.WriteLine("3 exit.");
input = Console.ReadLine();
switch (input) {
case "1":
await StartTaskAsync(l, s, rand, watch);
break;
case "2":
view.displayMap();
break;
case "exit":
input = "exit";
break;
default:
input = "exit";
break;
}
}
}
static async Task StartTaskAsync(Roboter l, Roboter s, Random r, Stopwatch watch) {
Task < string > lifesaverTask = StartLifesaverAsync(l, r, watch);
Task < string > superheroTask = StartSuperheroAsync(s, r, watch);
List < Task < string >> taskList = new List < Task < string >> {
lifesaverTask,
superheroTask
};
/* Task<string> completedTask = await Task.WhenAny(taskList);
string result = await completedTask;
Console.WriteLine(result);*/
while (taskList.Count > 0) {
Task < string > taskResult = await Task.WhenAny(taskList);
if (taskResult == lifesaverTask) {
Console.WriteLine(lifesaverTask.Result);
} else if (taskResult == superheroTask) {
Console.WriteLine(superheroTask.Result);
}
taskList.Remove(taskResult);
}
}
static async Task < string > StartLifesaverAsync(Roboter l, Random r, Stopwatch watch) {
return await Task.Run(() => {
int steps = 1;
string[] directions = new string[] {
"NORTH",
"SOUTH",
"WEST",
"EAST"
};
watch.Start();
while (l.state != true) {
int zufall = r.Next(0, 4);
string direction = directions[zufall];
l.move(direction, l.map);
steps++;
}
watch.Stop();
long time = watch.ElapsedMilliseconds;
l.reset(1, 1);
return "Lifesaver: steps: " + (steps).ToString() + ", time: " + time.ToString() + "ms";
});
}
static async Task < string > StartSuperheroAsync(Roboter s, Random r, Stopwatch watch) {
return await Task.Run(() => {
int steps = 1;
string[] directions = new string[] {
"NORTH",
"NORTH",
"NORTH",
"NORTH",
"NORTH",
"NORTH",
"SOUTH",
"WEST",
"EAST"
};
watch.Start();
while (s.state != true) {
int zufall = r.Next(0, 9);
string direction = directions[zufall];
s.move(direction, s.map);
steps++;
}
watch.Stop();
long time = watch.ElapsedMilliseconds;
s.reset(20, 20);
return "Superhero: steps: " + (steps).ToString() + ", time: " + time.ToString() + "ms";
});
}
}
I think there are at least two things a play here.
A. while(...)
while(something != true){ can be dangerous because they can run forever. You don't say how long "the program freezes" for so I'm not sure, but you may have a case where s.move(direction,s.map); never sets s.State to true.
You may want to protect yourself. Here's one option:
...
steps++;
if(steps > 1_000) throw ApplicationException("Something went wrong. Too many steps taken");
B. await Task.Run
await Task.Run(() => { will wait until the work is done.
If you want to start the task but not wait until it's finished don't await immediately. Here's a little example:
using System.Diagnostics;
var sw = Stopwatch.StartNew();
var t = Task.Run(() => { Console.WriteLine("Task 1 starting... Thread ID: {0}",Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);// Simulate a computationally heavy work
Console.WriteLine("Task 1 ending... thread ID: {0}",Thread.CurrentThread.ManagedThreadId);
});
var t2 = Task.Run(() => { Console.WriteLine("Task 2 starting... thread ID: {0}",Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);// Simulate a computationally heavy work
Console.WriteLine("Task 2 ending... thread ID: {0}",Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("After starting both tasks. Task thread ID: {0}",Thread.CurrentThread.ManagedThreadId);
await Task.WhenAll(t, t2);
Console.WriteLine("Tasks finished. Task thread ID: {0}",Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(sw.Elapsed);
prints
After starting both tasks. Task thread ID: 1
Task 2 starting... thread ID: 5
Task 1 starting... Thread ID: 3
Task 1 ending... thread ID: 3
Task 2 ending... thread ID: 5
Tasks finished. Task thread ID: 5
00:00:02.0351754
Here I have simple code written in C#.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Reactive.Subjects;
namespace ReactiveProgramming
{
class Program
{
static void Main(string[] args)
{
var generateSeq = new GenerateSequence();
Console.WriteLine("Hello World!");
generateSeq.Sequence.Subscribe(val =>
{
Console.WriteLine(val);
// it works if I remove below two lines ...
Console.SetCursorPosition(0, Console.CursorTop - 1);
Console.Write("\r" + new string(' ', Console.WindowWidth) + "\r");
});
generateSeq.Run();
}
}
class GenerateSequence
{
public Subject<int> Sequence = new Subject<int>();
public void Run(int runTimes = 10)
{
ConsoleKeyInfo cki;
Task.Run(() => runForTimes(10));
do
{
cki = Console.ReadKey();
} while (cki.Key != ConsoleKey.Escape);
}
public void runForTimes(int runTimes = 10)
{
for (var i = 0; i < 10; i++)
{
Sequence.OnNext(i);
Thread.Sleep(1000);
}
}
}
}
But instead of printing sequence on top of each other, it just freeze the output after first emit.
And tested in Linux too ... same output.
If I remote these lines Console.SetCursorPosition and Console.Write("\r" + new string(' ', Console.WindowWidth) + "\r") from subscribe ... it works and print all numbers on screen one after another but I want to print on top of each other ...
But if I change my Main function like this:
static void Main(string[] args)
{
var generateSeq = new GenerateSequence();
Console.WriteLine("Hello World!");
generateSeq.Sequence.Subscribe(val =>
{
Console.WriteLine(val);
// Console.SetCursorPosition(0, Console.CursorTop - 1);
// Console.Write("\r" + new string(' ', Console.WindowWidth) + "\r");
});
generateSeq.Run();
}
Where I have commented those two lines ... output is as follows ...
But instead of output in sequence like second image, I want to print the output at the same position. Just over write the new output over the old one
Note: I am running it on Macbook Pro (Big Sur), it happens with .net core 3.1 or .net 5.0 and using iTerm as console emulator
If I were writing this, I'd go with this implementation:
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
IObservable<System.ConsoleKeyInfo> keys =
Observable
.Start(() => Console.ReadKey());
await
Observable
.Interval(TimeSpan.FromSeconds(1.0))
.Take(10)
.TakeUntil(keys)
.Do(x =>
{
Console.WriteLine(x);
Console.SetCursorPosition(0, Console.CursorTop - 1);
},
() => Console.SetCursorPosition(0, Console.CursorTop + 1));
Console.WriteLine("Bye World!");
}
Wherever possible you should avoid using subjects.
SetCursorPosition works perfectly fine when not called from another thread. you can use an asynchronous approach to solve the problem instead of using Task.Run
class Program
{
static void Main(string[] args)
{
var generateSeq = new GenerateSequence();
Console.WriteLine("Hello World!");
generateSeq.Sequence.Subscribe(val =>
{
Console.WriteLine(val);
// move cursor back to previous line
Console.SetCursorPosition(0 ,Console.CursorTop - 1);
});
// start background operation
generateSeq.Run();
}
}
class GenerateSequence
{
public readonly Subject<int> Sequence = new();
public void Run(int runTimes = 10)
{
ConsoleKeyInfo cki;
// create a cancelation token, because if the user presses
// Escape key we don't need to run our background task
// anymore and the task should be stopped.
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
// we can not use await keyword here because we need to
// listen to ReadKey functions in case the user wants to
// stop the execution. without the await, task will run in
// the background asynchronously
var task = RunForTimes(runTimes,token);
// wait for the Escape key to cancel the execution or stop it
// if it's already running
do
{
cki = Console.ReadKey();
} while (cki.Key != ConsoleKey.Escape && !task.IsCompleted);
// cancel the background task if it's not compeleted.
if (!task.IsCompleted)
tokenSource.Cancel();
// Revert CursorPosition to the original state
Console.SetCursorPosition(0, Console.CursorTop + 1);
Console.WriteLine("Execution ends");
}
// we use an async task instead of a void to run our background
// job Asynchronously.
// the main difference is, we should not use a separate thread
// because we need to be on the main thread to safely access the Console (to read or write)
private async Task RunForTimes(int runTimes, CancellationToken token)
{
for (var i = 0; i < runTimes; i++)
{
Sequence.OnNext(i);
await Task.Delay(1000, token);
// exit the operation if it is requested
if (token.IsCancellationRequested) return;
}
}
}
This is a follow up question from this question: How do you run a variable number of concurrent parametrizable infinite loop type of threads in C#?
Say I have a value taskLimit (assume 20), of no of simultaneous Tasks "MyTask" which are created in the RunAsync method below:
protected override async Task RunAsync(CancellationToken cancellationToken)
{
var tasks = new List<Task>();
try
{
for (int i = 0; i < taskLimit; i++)
{
tasks.Add(MyTask(cancellationToken, i);
}
await Task.WhenAll(tasks);
}
catch (Exception e)
{
//Exception Handling
}
}
public async Task MyTask(CancellationToken cancellationToken, int a)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
//long running code, if possible check for cancellation using the token
//Do something useful here. Very Processor and IO heavy. Takes 5-10 minutes to complete.
//SomeHeavyTask can only concurrently run upto a limit of say 5. Implement a token system of sorts
while(freeTokens<1)
{
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
}
freeTokens = freeTokens-1;
SomeHeavyTask(cancellationToken);
freeTokens = freeTokens+1;
//sleep for an independently parameterizable period, then wake up and repeat
await Task.Delay(TimeSpan.FromHours(parametrizableTaskDelay[i]), cancellationToken);
}
catch (Exception e)
{
//Exception Handling
}
}
}
Is it possible to do such a thing? Is there a better more formal approach supported natively in C# to achieve the same thing? Please note the essence of this question is that there are substantially less freeTokens than the taskLimit. And that each MyTask spends only 10% time in SomeHeavyTask(), and most of the time is spent in await Task.Delay().
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq; - then you can do this:
int taskLimit = 500;
int maxConcurrent = 5;
IObservable<Unit> query =
Observable
.Range(0, taskLimit)
.Select(x => Observable.FromAsync(ct => SomeHeavyTask(ct)))
.Merge(maxConcurrent);
await query;
That's a lot easier to work with in my book.
Another option:
var block = new ActionBlock<int>(x => SomeHeavyTask(cancellationToken, x),
new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 20,
CancellationToken = cancellationToken
});
for (int i = 0; i < 100; i++)
await block.SendAsync(i, cancellationToken);
block.Complete();
await block.Completion;
You could use a SemaphoreSlim to limit the number of tasks that are working concurrently (you will still have taskLimit Tasks active, but only a limited number of those will be doing the heavy work simultaneously; I assume this is what you want).
This is best demonstrated with a sample console app. If you run this you'll see from the output that a maximum of 5 "heavy tasks" are active simultaneously.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
class Program
{
static async Task Main()
{
Console.WriteLine("Starting");
// Cancel after 30 seconds for demo purposes.
using var source = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await RunAsync(source.Token);
Console.WriteLine("Stopped.");
Console.ReadLine();
}
public static async Task RunAsync(CancellationToken cancellationToken)
{
int taskLimit = 20;
int concurrencyLimit = 5;
var sem = new SemaphoreSlim(concurrencyLimit);
var tasks = new List<Task>();
try
{
for (int i = 0; i < taskLimit; i++)
{
int p = i; // Prevent modified closure.
tasks.Add(Task.Run(() => MyTask(cancellationToken, p, sem)));
}
await Task.WhenAll(tasks);
}
catch (OperationCanceledException)
{
Console.WriteLine("Task(s) were cancelled.");
}
catch (Exception e)
{
// Exception Handling
}
}
public static async Task MyTask(CancellationToken cancellationToken, int a, SemaphoreSlim sem)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
await sem.WaitAsync(cancellationToken);
try
{
someHeavyTask(cancellationToken, a);
}
finally
{
sem.Release();
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was cancelled.");
return;
}
catch (Exception e)
{
//Exception Handling
}
}
}
static int heavyTaskCount;
static void someHeavyTask(CancellationToken cancel, int a)
{
int n = Interlocked.Increment(ref heavyTaskCount);
Console.WriteLine("Starting heavy task. Number of simultaneous heavy tasks = " + n);
// Simulate work. Make the work for each task take varying time by using 'a' for the sleep.
for (int i = 0; i < 20 && !cancel.IsCancellationRequested; ++i)
{
Thread.Sleep(100 + a*10);
}
n = Interlocked.Decrement(ref heavyTaskCount);
Console.WriteLine("Finishing heavy task. Number of simultaneous heavy tasks = " + n);
}
}
}
The core of this is controlled by the semaphore in the code here:
await sem.WaitAsync(cancellationToken);
try
{
someHeavyTask(cancellationToken, a);
}
finally
{
sem.Release();
}
Like #mjwills said, you can use a C# semaphore to manage concurrent access to resources. (random example)
I do recommend looking at existing solutions first though. For example, Hangfire.
You can store its state inside SF if needed.
This question already has answers here:
How to delay 'hot' tasks so they can processed in a set order
(2 answers)
Closed 4 years ago.
I need to first create new task then do some remaining work and then start the task that works with its result.
Simplified example:
static int value;
static async Task work1()
{
do
{
int i;
for (i = 0; i < 10000000; i++) {} // some calculations
Console.WriteLine("result1: " + value + " i: " + i);
await Task.Delay(2000).ConfigureAwait(false);
} while (condition);
}
static async Task work2()
{
do
{
int i;
for (i = 0; i < 10000000; i++) {} // some calculations
Console.WriteLine("result2: " + value + " i: " + i);
await Task.Delay(2000).ConfigureAwait(false);
} while (condition);
}
static void Main(string[] args)
{
Task task;
int tempvalue = 100;
if (condition1)
{
tempvalue *= 10;
task = new Task(() => work1());
} else
{
tempvalue -= 5;
task = new Task(() => work2());
}
if (tempvalue > 100)
{
value = 5;
} else
{
value = tempvalue;
}
task.Start();
// immediately do remaining work
}
this code does exactly what I need but compiler shows following warning:
Warning CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
on line:
Task task = new Task(() => work());
should I rather use it like so? Is there any difference?
Task task = new Task(async () => await work());
This is not a duplicate of How to delay 'hot' tasks so they can processed in a set order because after task.Start(); it should do remaining work immediately.
Func<Task> f = () => work();
// do stuff
f(); // blocks thread until work1() or work2() hits await
// do remaining work
The async keyword means that the task within your task is asynchronous and using await will mean that you want to wait for the method work to finish.
You could also use Task.Wait() in order to wait for the method to finish it's execution.
But using async await is the better way to do it because it's not blocking the main thread.
I have a console app that is making HTTP queries and adding/updating products in my database according to response. Some fail and need to be retried a few times.
The way I came up with was to use a dictionary to store the product ID and a Task. Then I can check all the task results and re-run.
This is working but it strikes me as inefficient. Tasks are not being re-created until all tasks have finished. It would be more efficient if they were immediately restarted but I can't figure out how to do this. Also every retry involves a query to the database as only the ID is stored.
I made small app that shows how I am currently retrying failed requests.
Can someone suggest a more efficient method for retrying?
class Program
{
private static void Main(string[] args)
{
HttpQuery m = new HttpQuery();
var task = Task.Run(() => m.Start());
Task.WaitAll(task);
Console.WriteLine("Finished");
Console.ReadLine();
}
}
class HttpQuery
{
public async Task Start()
{
// dictionary where key represent reference to something that needs to be processed and bool whether it has completed or not
ConcurrentDictionary<int, Task<bool>> monitor = new ConcurrentDictionary<int, Task<bool>>();
// start async tasks.
Console.WriteLine("starting first try");
for (int i = 0; i < 1000; i++)
{
Console.Write(i+",");
monitor[i] = this.Query(i);
}
// wait for completion
await Task.WhenAll(monitor.Values.ToArray());
Console.WriteLine();
// start retries
// number of retries per query
int retries = 10;
int count = 0;
// check if max retries exceeded or all completed
while (count < retries && monitor.Any(x => x.Value.Result == false))
{
// make list of numbers that failed
List<int> retryList = monitor.Where(x => x.Value.Result == false).Select(x => x.Key).ToList();
Console.WriteLine("starting try number: " + (count+1) + ", Processing: " + retryList.Count);
// create list of tasks to wait for
List<Task<bool>> toWait = new List<Task<bool>>();
foreach (var i in retryList)
{
Console.Write(i + ",");
monitor[i] = this.Query(i);
toWait.Add(monitor[i]);
}
// wait for completion
await Task.WhenAll(toWait.ToArray());
Console.WriteLine();
count++;
}
Console.WriteLine("ended");
Console.ReadLine();
}
public async Task<bool> Query(int i)
{
// simulate a http request that may or may not fail
Random r = new Random();
int delay = i * r.Next(1, 10);
await Task.Delay(delay);
if (r.Next(0,2) == 1)
{
return true;
}
else
{
return false;
}
}
}
You can create another method and wrap all these ugly retry logic. All of that ugly code goes away :)
public async Task Start()
{
const int MaxNumberOfTries = 10;
List<Task<bool>> tasks = new List<Task<bool>>();
for (int i = 0; i < 1000; i++)
{
tasks.Add(this.QueryWithRetry(i, MaxNumberOfTries));
}
await Task.WhenAll(tasks);
}
public async Task<bool> QueryWithRetry(int i, int numOfTries)
{
int tries = 0;
bool result;
do
{
result = await Query(i);
tries++;
} while (!result && tries < numOfTries);
return result;
}