I can't get my CPU to 100% using Async method in C#, my average is around 50%. I am unsure if there is a lock, garbage collection, or other behind-the-scene tasks that are slowing my App.
The task and its results are pure calculations, there are no I/O or network requests.
Does my code need any enhancements?
for (int i = 0; i < loops; i++)
{
List<Task<List<Distance>>> DistanceTask = new List<Task<List<Distance>>>();
foreach (var item in dstpart)
{
List<string> itemKeywords = item.Keywords.Split(',').Take(10).ToList();
DistanceTask.Add(Task.Run(() => CalculateDistances(itemKeywords)));
//one task takes around 5sec to complete, no IO or network requests
}
var results = await Task.WhenAll(DistanceTask);
foreach (var r in results)
{
//evaluate the results, no IO or network operations here, It's few (ms)
}
}
There's nothing wrong with the pattern. This simplification pegs all my CPUs
var loops = 100;
var dstpart = Enumerable.Range(1, 100).ToList();
for (int i = 0; i < loops; i++)
{
List<Task> tasks = new List<Task>();
foreach (var item in dstpart)
{
tasks.Add(Task.Run(() =>Spin() ));
//one task takes around 5sec to complete, no IO or network requests
}
await Task.WhenAll(tasks);
}
void Spin()
{
var sw = new Stopwatch();
sw.Start();
while (sw.ElapsedMilliseconds < 5000)
Thread.SpinWait(10000);
}
Related
I have a problem, that I could not find any answear yet. And this is my first project with use of threading and Tasks. When my task is cancelled, it keeps executing time consuming method.
Right now I have not idea how to stop execution of the method together with the task.
Below is a loop, that runs tasks. Every single task is running a ParseHorseData method, that runs also several other methods. Execution of them takes sometimes a lot of time.
After the task is cancelled, before await Task.WhenAll(tasks); is completed, it takes a lot of time.
So, as in the question, is there a way to cancel execution of method in cancelled task?
List<Task> tasks = new List<Task>();
int loopCounter = 0;
int taskCounter = 0;
//for all races in the file
for (int i = 0; i < _allRaces.Count; i ++)
{
int j = i;
if (TaskCancellation == true)
{
break;
}
Task task = Task.Run(async () =>
{
while (!_cancellationToken.IsCancellationRequested)
{
loopCounter++;
ProgressBarTick("Requesting historic data", loopCounter, _allRaces.Count, 0);
//if the race is from 2018
if (_allRaces[j].RaceDate.Year == 2018)
{
Category = _allRaces[j].RaceCategory;
Distance = _allRaces[j].RaceDistance.ToString();
//for all horses in the race
for (int h = 0; h < _allRaces[j].HorseList.Count; h++)
{
HorseDataWrapper horse = new HorseDataWrapper();
//TIME CONSUMING
horse = ParseHorseData(_allRaces[j].HorseList[h], _allRaces[j].RaceDate);
_allRaces[j].HorseList[h] = horse;
}
}
taskCounter++;
if (loopCounter >= _allRaces.Count)
{
ProgressBarTick("Testing on historic data", taskCounter, _allRaces.Count, 0);
}
}
}, _tokenSource.Token);
tasks.Add(task);
}
try
{
await Task.WhenAll(tasks);
}
catch (TaskCanceledException)
{
//
}
finally
{
_tokenSource.Dispose();
}
is there a way to cancel execution of method in cancelled task?
All cancellation is cooperative. The method being executed must pass the CancellationToken into the methods that it calls, and either:
Periodically poll for cancellation using ThrowIfCancellationRequested. This approach is more appropriate for CPU-bound loops.
Take an action on cancellation using Register. This approach is more appropriate for interfacing with non-CancellationToken-based cancellation systems.
In this case, it sounds like polling is appropriate. I strongly recommend polling via ThrowIfCancellationRequested and not IsCancellationRequested, because when a task is canceled, it should throw an OperationCanceledException when awaited. This is how the calling code knows it has been canceled.
Example:
Task task = Task.Run(async () =>
{
while (true)
{
_cancellationToken.ThrowIfCancellationRequested();
...
//for all horses in the race
for (int h = 0; h < _allRaces[j].HorseList.Count; h++)
{
_cancellationToken.ThrowIfCancellationRequested();
HorseDataWrapper horse = new HorseDataWrapper();
horse = ParseHorseData(_allRaces[j].HorseList[h], _allRaces[j].RaceDate);
_allRaces[j].HorseList[h] = horse;
}
...
}
});
I am trying to throttle the number of async tasks that I submit to an external database server. I created the following test code which should take 25 seconds to execute (150 tasks * 5 seconds / 30 max concurrent tasks = 25 seconds).
private async void ThrottlingTest()
{
TaskScheduler scheduler = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 30).ConcurrentScheduler;
Stopwatch sw = new Stopwatch();
int numTasks = 150;
Task[] tasks = new Task[numTasks];
for (int i = 0; i < numTasks; i++)
{
tasks[i] = Task.Factory.StartNew(() => Task.Delay(5000), CancellationToken.None, TaskCreationOptions.None, scheduler).Unwrap();
}
sw.Start();
await Task.WhenAll(tasks);
sw.Stop();
long duration = sw.ElapsedMilliseconds;
}
However, this code only takes 5 seconds to execute indicating my throttling is not working. What am I doing wrong? I suspect Task.Delay is running using the default scheduler. If so, what delay mechanism can I test with?
I am trying to throttle the number of async tasks that I submit to an external database server.
TaskSchedulers can only be used to control the number of running tasks. Asynchronous tasks do not "run". They can be "in progress", but they are not actually running code while being so. Put another way, TaskSchedulers only work as expected with synchronous tasks. Or if you want to use this terminology, TaskSchedulers were designed for Delegate Tasks; they do not understand Promise Tasks.
To throttle asynchronous operations, use SemaphoreSlim instead:
private async void ThrottlingTest()
{
SemaphoreSlim mutex = new SemaphoreSlim(30);
Stopwatch sw = new Stopwatch();
int numTasks = 150;
Task[] tasks = new Task[numTasks];
for (int i = 0; i < numTasks; i++)
tasks[i] = TestAsync();
sw.Start();
await Task.WhenAll(tasks);
sw.Stop();
long duration = sw.ElapsedMilliseconds;
async Task TestAsync()
{
await mutex.WaitAsync();
try { await Task.Delay(5000); }
finally { mutex.Release(); }
}
}
I've created a multi task program. This program has around 20 main tasks and each of them calls some sub tasks to operate file I/Os. I wanted each main task to repeat periodically every 500ms, so I enterd the code Task.Delay(500).
The problem is Task.Delay delays a lot more than 500ms sometimes. There is a case it delays more than 3 seconds.
How can I fix it?
The original progam is so big that I created a sample program below.
(1) If Task.Delay is on, over-delay happens.
(2) If Thead.Sleep is on, over-delay doesn't happen.
ThreadPool.SetMinThreads() doesn't seem to resolve it.
Thanks.
class Program
{
const int DELAY_TIME = 500;
const int TASKS = 100;
const int WAITS = 100;
const int WARNING_THRESHOLD = 100;
static void Main(string[] args)
{
//ThreadPool.SetMinThreads(workerThreads: 200, completionPortThreads: 200);
Console.WriteLine("*** Start...");
Test();
Console.WriteLine("*** Done!");
Console.ReadKey();
}
private static void Test()
{
List<Task> tasks = new List<Task>();
for (int taskId = 0; taskId < TASKS; taskId++)
{
tasks.Add(DelaysAsync(taskId));
}
Task.WaitAll(tasks.ToArray());
}
static async Task DelaysAsync(int taskId)
{
await Task.Yield();
Stopwatch sw = new Stopwatch();
for (int i = 0; i < WAITS; i++)
{
sw.Reset();
sw.Start();
await Task.Delay(DELAY_TIME).ConfigureAwait(false); // (1)
//Thread.Sleep(DELAY_TIME); // (2)
sw.Stop();
Console.Write($"Task({taskId})_iter({i}) Elapsed={sw.ElapsedMilliseconds}");
if (sw.ElapsedMilliseconds > DELAY_TIME + WARNING_THRESHOLD)
{
Console.WriteLine(" *********** Too late!! ************");
}
else
{
Console.WriteLine();
}
}
}
}
I’ve run your test, with .NET 4.6.1 and VS 2017. Here on Xeon E3-1230 v3 CPU it never printed “Too late”, the Elapsed value was within 498-527 ms.
The Thread.Sleep version performed very similarly, 500-528ms per sleep, however the total execution time was much longer because the runtime refused to create 100 OS threads, that’s way too many, so less than 100 DelaysAsync functions ran in parallel. The debugger showed me there were 27 worker threads in Thread.Sleep version and only 9 worker threads in Task.Delay version.
I think you have other apps on your PC creating too many threads and consuming too much CPU. Windows tries to load balance threads evenly so when the whole system is CPU bound, more native threads = more CPU time and therefore less jitter.
If that’s your case and you want to prioritize your app in the scheduler, instead of using Thread.Sleep and more threads, raise the priority of your process.
It seems that I could find the answer. I changed the previous sample program like below. The main difference is using StopWatch or DateTime to measure time durations.
In StopWatch version, many delays happen.
In DateTime version, no or at least very little delays happen(s).
I guess that the cause is the contention of Timer that is used by both StopWatch and Task.Delay. I concluded that I should not use StopWatch and Task.Delay together.
Thank you.
class Program
{
const int DELAY_TIME = 500;
const int TASKS = 100;
const int WAITS = 100;
const int WARNING_THRESHOLD = 500;
static void Main(string[] args)
{
using (Process p = Process.GetCurrentProcess())
{
p.PriorityClass = ProcessPriorityClass.RealTime;
//ThreadPool.SetMinThreads(workerThreads: 200, completionPortThreads: 200);
int workerThreads;
int completionPortThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Console.WriteLine($"{workerThreads}, {completionPortThreads}");
Console.WriteLine("*** Start...");
Test();
Console.WriteLine("*** Done!");
Console.ReadKey();
}
}
private static void Test()
{
int totalCount = 0;
List<Task<int>> tasks = new List<Task<int>>();
for (int taskId = 0; taskId < TASKS; taskId++)
{
//tasks.Add(DelaysWithStopWatchAsync(taskId)); // many delays
tasks.Add(DelaysWithDateTimeAsync(taskId)); // no delays
}
Task.WaitAll(tasks.ToArray());
foreach (var task in tasks)
{
totalCount += task.Result;
}
Console.WriteLine($"Total counts of deday = {totalCount}");
}
static async Task<int> DelaysWithStopWatchAsync(int taskId)
{
await Task.Yield();
int count = 0;
Stopwatch sw = new Stopwatch();
for (int i = 0; i < WAITS; i++)
{
sw.Reset();
sw.Start();
await Task.Delay(DELAY_TIME).ConfigureAwait(false); // (1)
//Thread.Sleep(DELAY_TIME); // (2)
sw.Stop();
Console.Write($"task({taskId})_iter({i}) elapsed={sw.ElapsedMilliseconds}");
if (sw.ElapsedMilliseconds > DELAY_TIME + WARNING_THRESHOLD)
{
Console.WriteLine($" *********** Too late!! ************");
count++;
}
else
{
Console.WriteLine();
}
}
return count;
}
static async Task<int> DelaysWithDateTimeAsync(int taskId)
{
await Task.Yield();
int count = 0;
for (int i = 0; i < WAITS; i++)
{
DateTime start = DateTime.Now;
await Task.Delay(DELAY_TIME).ConfigureAwait(false); // (1)
//Thread.Sleep(DELAY_TIME); // (2)
DateTime end = DateTime.Now;
int duration = (end - start).Milliseconds;
Console.Write($"Task({taskId})_iter({i}) Elapsed={duration}");
if (duration > DELAY_TIME + WARNING_THRESHOLD)
{
Console.WriteLine($" *********** Too late!! ************");
count++;
}
else
{
Console.WriteLine();
}
}
return count;
}
}
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;
}
I have a loop that creates 5 Tasks. How can I insert a Delay of 5 seconds between each Task. I don't know how to fit Task.Delay(5000) in there.
var tasks = new List<Task<int>>();
for (var i = 0; i < 5; i++)
{
tasks.Add(ProcessQueueAsync());
}
await Task.WhenAll(tasks);
My ProcessQueAsync method calls a server, retrieves data and returns and int.
private async Task<int> ProcessQueAsync()
{
var result = await CallToServer();
return result.Count;
}
for (var i = 0; i < 5; i++)
{
tasks.Add(ProcessQueueAsync());
await Task.Delay(5000);
}
Or:
for (var i = 0; i < 5; i++)
{
await ProcessQueueAsync();
await Task.Delay(5000);
}
Depending on that you want.
If you want the tasks to run one after the other, with a 5 second delay, you should perhaps look at Task.ContinueWith instead of using Task.WhenAll. This would allow you to run tasks in serial rather than in parallel.