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(); }
}
}
Related
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);
}
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'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 application with have two threads as:
public static async void Thread1()
{
for (int i = 0; i < 100; i++)
{
Debug.WriteLine("Thread1 " + i);
await MyFunc();
}
}
public static async void Thread2()
{
for (int i = 0; i < 100; i++)
{
Debug.WriteLine("Thread2 " + i);
await MyFunc();
}
}
public static void Main(string[] args)
{
MainAsync(args).GetAwaiter().GetResult();
}
private static async Task MainAsync(string[] args)
{
Console.WriteLine("Before start thread");
Thread tid1 = new Thread(Thread1);
Thread tid2 = new Thread(Thread2);
tid1.Start();
tid2.Start();
}
public static async Task MyFunc()
{
//do something
}
However, when the application run and terminates, it seems that only each thread is run just once as I see only below things in output:
Before start thread
Thread1 0
Thread2 0
//some thing from MyFunc
I expect or rather wannt to run each thread till the for loop.It seems to me that the for loop continues to run despite the await.
If yes, what could be other probable approach.
Any leads would be helpful.
You aren't doing anything to wait for the threads. The main routine will just continue on until it returns to the O/S, which will kill the process and any child threads. Since you aren't doing anything else, this happens almost immediately, cutting both threads' lives short.
If you want to wait for the threads to finish, you can refer to this answer and write some variation of
while (thread1.IsAlive || thread2.IsAlive)
{
//Do something to wait
}
...before exiting.
That being said, you should probably using Tasks instead of threads, e.g.
public static async Task Task1()
{
for (int i = 0; i < 100; i++)
{
Debug.WriteLine("Task1 " + i);
await MyFunc();
}
}
public static async Task Task2()
{
for (int i = 0; i < 100; i++)
{
Debug.WriteLine("Task2 " + i);
await MyFunc();
}
}
And then to execute and wait for both of them:
Task.WaitAll
(
new[]
{
Task1(),
Task2()
}
);
See this code in action on DotNetFiddle
See also What is the difference between tasks and threads?
You seem to have a lot of confusion about the role of threads and tasks, so it's a good idea to read up about it. Steven Cleary has a nice write-up about this. "There Is No Thread"
From the comments, it seems that your actual intention here is to run two async tasks in parallel, then to wait until they are both finished.
If you want to wait for two async tasks to complete in parallel, make sure your async methods actually return Task then:
Task task1 = DoSomethingAsync(); //don't await
Task task2 = DoSomethingElseAsync(); //don't await
then you can wait asynchronously for Task.WhenAll:
await Task.WhenAll(task1,task2);
You really don't need to be involving Thread at all.
Use async Task instead of async void
private static async Task MainAsync(string[] args)
{
Console.WriteLine("Before start thread");
var task1 = Thread1();
var task2 = Thread2();
var taskList = new [] { task1, task2 };
Task.WaitAll(taskList);
}
I got a requirement where I need to process two Threads simultaneously in a ForLoop.
For Example :
private void BtnThreading_Click(object sender, EventArgs e)
{
for (int i = 0; i < 20; i++)
{
Thread thread1 = new Thread(ProcessA);
thread1.Start();
thread1.Join();
Thread thread2 = new Thread(ProcessB);
thread2.Start();
}
}
private void ProcessA()
{
Thread.Sleep(10000);
}
private void ProcessB()
{
Thread.Sleep(20000);
}
For the First Time in the for-loop,Let's say the ProcessA() takes 10 Seconds to complete and I need to wait till the ProcessA() to finish using thread1.Join(); to Start Processing ProcessB().Later the ProcessB() starts and it will take 20 seconds to finish.
So,In the mean while ProcessA() again starts and thread1.Join(); statement will wait until ProcessA() finishes.Here I need to wait also for the previous ProcessB() to finish.
so finally,I want the ProcessB() to wait for the Previous Thread of ProcessB()
Sorry for my Bad English !!! :)
Unless your really need to do low level programming you should not use Thread objects directly. Each thread is quite expensive in terms of resources. Instead .NET provides several high level abstractions to do asynchronous and parallel programming. Your loop can be built using async and await and will then execute on thread pool threads. Here is an example:
Instead of using Thread.Sleep to simulate delay you have to use Task.Delay:
async Task ProcessA() {
await Task.Delay(10000);
}
async Task ProcessB() {
await Task.Delay(20000);
}
The loop:
var task = Task.Delay(0); // No operation task to simplify loop.
for (var i = 0; i < 20; i += 1) {
await ProcessA();
await task;
task = ProcessB();
}
await task;
It looks like you don't really need ProcessA to execute in a different thread at all - but you do need to keep track of your previous ProcessB thread. So something like:
Thread previousThread = null;
for (int i = 0; i < 20; i++)
{
ProcessA();
if (previousThread != null)
{
previousThread.Join();
}
previousThread = new Thread(ProcessB);
previousThread.Start();
}
// Possibly join on previousThread here too
Note that your method name suggests that you're doing this in a UI thread - which you really, really shouldn't. Don't block the UI thread for any length of time - and remember that Join is a blocking call.
You can give this code a try, but again it is not production ready..!!!
Thread thread1 = null;
Thread thread2 = null;
private void BtnThreading_Click(object sender, EventArgs e)
{
for (int i = 0; i < 20; i++)
{
thread1 = new Thread(ProcessA);
thread1.Start();
thread1.Join();
if (thread2 != null)
thread2.Join();
thread2 = new Thread(ProcessB);
thread2.Start();
}
}