Task starts with delay - c#

I create and start task in following way:
Task task = new Task(() => controller.Play());
task.Start();
For some reason, sometimes task get started with around 7-10 seconds delay.
I use 6 tasks in parallel, max number of tasks is 32767 and available 32759
which is what i log before i create task so it can't be that max number of tasks is reached. I write log at the first line of code in controller.Play() method that task should execute, so there is no lock or anything that could make task to wait.

Long running tasks, like your deserialization of 100MB that takes 10 seconds, should be, hm, well, run as long-running tasks :-)
Long-running tasks are, as per the current implementation, always run on a dedicated thread and they do not put pressure on the thread-pool.
In your case, you perhaps only two tasks - the deserialization and the player. The TaskScheduler works under the assumption that tasks are short-lived, and in this case, it obviously schedules the "player" task to run after the "deserializaion" one.

Related

How does asynchronous programming work with threads when using Thread.Sleep()?

Presumptions/Prelude:
In previous questions, we note that Thread.Sleep blocks threads see: When to use Task.Delay, when to use Thread.Sleep?.
We also note that console apps have three threads: The main thread, the GC thread & the finalizer thread IIRC. All other threads are debugger threads.
We know that async does not spin up new threads, and it instead runs on the synchronization context, "uses time on the thread only when the method is active". https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model
Setup:
In a sample console app, we can see that neither the sibling nor the parent code are affected by a call to Thread.Sleep, at least until the await is called (unknown if further).
var sw = new Stopwatch();
sw.Start();
Console.WriteLine($"{sw.Elapsed}");
var asyncTests = new AsyncTests();
var go1 = asyncTests.WriteWithSleep();
var go2 = asyncTests.WriteWithoutSleep();
await go1;
await go2;
sw.Stop();
Console.WriteLine($"{sw.Elapsed}");
Stopwatch sw1 = new Stopwatch();
public async Task WriteWithSleep()
{
sw1.Start();
await Task.Delay(1000);
Console.WriteLine("Delayed 1 seconds");
Console.WriteLine($"{sw1.Elapsed}");
Thread.Sleep(9000);
Console.WriteLine("Delayed 10 seconds");
Console.WriteLine($"{sw1.Elapsed}");
sw1.Stop();
}
public async Task WriteWithoutSleep()
{
await Task.Delay(3000);
Console.WriteLine("Delayed 3 second.");
Console.WriteLine($"{sw1.Elapsed}");
await Task.Delay(6000);
Console.WriteLine("Delayed 9 seconds.");
Console.WriteLine($"{sw1.Elapsed}");
}
Question:
If the thread is blocked from execution during Thread.Sleep, how is it that it continues to process the parent and sibling? Some answer that it is background threads, but I see no evidence of multithreading background threads. What am I missing?
I see no evidence of multithreading background threads. What am I missing?
Possibly you are looking in the wrong place, or using the wrong tools. There's a handy property that might be of use to you, in the form of Thread.CurrentThread.ManagedThreadId. According to the docs,
A thread's ManagedThreadId property value serves to uniquely identify that thread within its process.
The value of the ManagedThreadId property does not vary over time
This means that all code running on the same thread will always see the same ManagedThreadId value. If you sprinkle some extra WriteLines into your code, you'll be able to see that your tasks may run on several different threads during their lifetimes. It is even entirely possible for some async applications to have all their tasks run on the same thread, though you probably won't see that behaviour in your code under normal circumstances.
Here's some example output from my machine, not guaranteed to be the same on yours, nor is it necessarily going to be the same output on successive runs of the same application.
00:00:00.0000030
* WriteWithSleep on thread 1 before await
* WriteWithoutSleep on thread 1 before first await
* WriteWithSleep on thread 4 after await
Delayed 1 seconds
00:00:01.0203244
* WriteWithoutSleep on thread 5 after first await
Delayed 3 second.
00:00:03.0310891
* WriteWithoutSleep on thread 6 after second await
Delayed 9 seconds.
00:00:09.0609263
Delayed 10 seconds
00:00:10.0257838
00:00:10.0898976
The business of running tasks on threads is handled by a TaskScheduler. You could write one that forces code to be single threaded, but that's not often a useful thing to do. The default scheduler uses a threadpool, and as such tasks can be run on a number of different threads.
The Task.Delay method is implemented basically like this (simplified¹):
public static Task Delay(int millisecondsDelay)
{
var tcs = new TaskCompletionSource();
_ = new Timer(_ => tcs.SetResult(), null, millisecondsDelay, -1);
return tcs.Task;
}
The Task is completed on the callback of a System.Threading.Timer component, and according to the documentation this callback is invoked on a ThreadPool thread:
The method does not execute on the thread that created the timer; it executes on a ThreadPool thread supplied by the system.
So when you await the task returned by the Task.Delay method, the continuation after the await runs on the ThreadPool. The ThreadPool typically has more than one threads available immediately on demand, so it's not difficult to introduce concurrency and parallelism if you create 2 tasks at once, like you do in your example. The main thread of a console application is not equipped with a SynchronizationContext by default, so there is no mechanism in place to prevent the observed concurrency.
¹ For demonstration purposes only. The Timer reference is not stored anywhere, so it might be garbage collected before the callback is invoked, resulting in the Task never completing.
I am not accepting my own answer, I will accept someone else's answer because they helped me figure this out. First, in the context of my question, I was using async Main. It was very hard to choose between Theodor's & Rook's answer. However, Rook's answer provided me with one thing that helped me fish: Thread.CurrentThread.ManagedThreadId
These are the results of my running code:
1 00:00:00.0000767
Not Delayed.
1 00:00:00.2988809
Delayed 1 second.
4 00:00:01.3392148
Delayed 3 second.
5 00:00:03.3716776
Delayed 9 seconds.
5 00:00:09.3838139
Delayed 10 seconds
4 00:00:10.3411050
4 00:00:10.5313519
I notice that there are 3 threads here, The initial thread (1) provides for the first calling method and part of the WriteWithSleep() until Task.Delay is initialized and later awaited. At the point that Task.Delay is brought back into Thread 1, everything is run on Thread 4 instead of Thread 1 for the main and the remainder of WriteWithSleep.
WriteWithoutSleep uses its own Thread(5).
So my error was believing that there were only 3 threads. I believed the answer to this question: https://stackoverflow.com/questions/3476642/why-does-this-simple-net-console-app-have-so-many-threads#:~:text=You%20should%20only%20see%20three,see%20are%20debugger%2Drelated%20threads.
However, that question may not have been async, or may not have considered these additional worker threads from the threadpool.
Thank you all for your assistance in figuring out this question.

Differences between Multithreading and Async

Note: Please read to the end before marking as duplicate. I've read the other answers, and they don't seem to answer my question.
I've seen various pictures and people point out and say that multithreading is different from asynchronous programming, by giving various analogies to restaurant workers and the like. But I've yet to see the difference with an actual example.
I tried this in C#:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncTest
{
class Program
{
static void RunSeconds(double seconds)
{
int ms = (int)(seconds * 1000);
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine($"Thread started to run for {seconds} seconds");
Thread.Sleep(ms);
stopwatch.Stop();
Console.WriteLine($"Stopwatch passed {stopwatch.ElapsedMilliseconds} ms.");
}
static async Task RunSecondsAsync(double seconds)
{
int ms = (int)(seconds * 1000);
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine($"Thread started to run for {seconds} seconds");
await Task.Run(() => Thread.Sleep(ms));
stopwatch.Stop();
Console.WriteLine($"Stopwatch passed {stopwatch.ElapsedMilliseconds} ms.");
}
static void RunSecondsThreaded(double seconds)
{
Thread th = new Thread(() => RunSeconds(seconds));
th.Start();
}
static async Task Main()
{
Console.WriteLine("Synchronous:");
RunSeconds(2.5); RunSeconds(2);
Console.WriteLine("\nAsynchronous:");
Task t1 = RunSecondsAsync(2.5); Task t2 = RunSecondsAsync(2);
await t1; await t2;
Console.WriteLine("\nMultithreading:");
RunSecondsThreaded(2.5); RunSecondsThreaded(2);
}
}
}
Results:
Synchronous:
Thread started to run for 2.5 seconds
Stopwatch passed 2507 ms.
Thread started to run for 2 seconds
Stopwatch passed 2001 ms.
Asynchronous:
Thread started to run for 2.5 seconds
Thread started to run for 2 seconds
Stopwatch passed 2002 ms.
Stopwatch passed 2554 ms.
Multithreading:
Thread started to run for 2.5 seconds
Thread started to run for 2 seconds
Stopwatch passed 2000 ms.
Stopwatch passed 2501 ms.
They yielded essentially the same results, behaviour-wise. So when and what exactly would I find different in the behaviour of a multithreaded program vs an asynchronous one?
I have various other issues to resolve:
In this image, for example:
What I don't get is that when you run an asynchronous program, it behaves practically identically to a multithreaded one, in that it seems to spend a similar amount of time. By the image above, it's addressing the asynchronous task in "breaks". If it does this, shouldn't it take longer for the asynchronous task to complete?
Let's say an asynchronous task which would normally complete 3 seconds synchronously while locking other tasks is run, should I not expect these tasks to finish in much longer than 3 seconds, given that it does other tasks on the side while taking breaks from my original task?
So why does it often take a similar asynchronously (ie. the usual 3 seconds)? And why does the program become "responsive": if the task is not being done on a separate thread, why does working on the task while working on other tasks on the side take only the expected 3 seconds?
The problem I have with the examples using workers in a restaurant (see top answer), is that in a restaurant, the cooking is done by the oven. In a computer, this analogy doesn't make much sense, as it's not clear why the oven isn't being treated as a separate "thread" but the people/workers are.
Furthermore, does a multithreaded application use more memory? And if it does, is it possible to create a simple application (ideally as similar to the one above) proving that it does?
Bit of a lengthy question, but the differences between multithreading and asynchronous programming are far from clear to me.
You can't use Thread.Sleep in async code, use
await Task.Delay(1000);
instead.
The async code uses a thread pool, any time the program awaits for some IO to complete, the thread is returned to the pool to do other stuff. Once the IO completes, the async method resumes at the line where it yielded the thread back to threadpool, continuing on.
When you manipulate with the Thread directly, you block and your code is no longer async, you also starve the threadpool as it is limited in the number of threads available.
Also throughout the lifetime of an async method, you are not guaranteed every line will be executed on the same thread. Generally after every await keyword the thread may change.
You never want to touch the Thread class in an async method.
By doing:
await Task.Run(() => Thread.Sleep(ms));
You force the TPL to allocate a thread out of the pool to block it, starving it.
By doing
await Task.Run(async () => await Task.Delay(ms));
you will essentially run on one or two threads from a pool even if you start it many times.
Running Task.Run() on synchronous code is mostly used for legacy calls that do not support async internally and the TPL just wraps the sync call in a pooled thread. To get the full advantages of async code you need to await a call that itself runs only async code internally.
Let me try to correlate your program with a real world example and then explain it.
Consider your program to be an IT office and your are the boss of it. Boss means the main thread which starts the program execution. The console can be considered as your diary.
Programs execution starts:
static async Task Main()
{
Process process = Process.GetCurrentProcess();
Console.WriteLine("Synchronous:");
You enter into the office from the main door and log "Synchronous:" into your diary.
Synchronous:
Calling method 'RunSeconds()'
RunSeconds(2.5); RunSeconds(2);
Let us assume 'RunSeconds()' is equivalent to a call from one of your projects client, however there is no one to attend the calls. So you attend both the calls.The thing to remember is you attend the calls one after the other as you are one person and total spent is close to 4.5 seconds.
Meanwhile you get a call from your home but you could not attend it because you were busy attending the client calls. Now coming to logging of the calls.You get a call you log it.Once it is completed you log the amount of time spent on call. And you do it twice for both the calls.
Thread started to run for 2.5 seconds
Stopwatch passed 2507 ms.
Thread started to run for 2 seconds
Stopwatch passed 2001 ms.
Console.WriteLine("\nAsynchronous:");
Then you log "Asynchronous:" into the diary
Calling method 'RunSecondsAsync()'
Task t1 = RunSecondsAsync(2.5); Task t2 = RunSecondsAsync(2);
await t1; await t2;
Let us assume 'RunSecondsAsync()' is again equivalent to a call from one of your projects client, however this time you have a Manager with a team of 10 call attendants who take the call. Here Manager is equivalent to the Task and each call attendant is a thread and collectively known as thread pool. Remember the manager by himself does not take any calls, he is just there to delegate calls to the call attendants and manage them
When the first call 'RunSecondsAsync(2.5)' comes in, the manager immediately assigns it to one of the call attendant and lets you know that the call has been addressed with the help of task object as return. You again get an immediate second call 'RunSecondsAsync(2)', which the manager immediately assigns to another call attendant and both the calls are handled simultaneously.
However you want to log the amount of time spent on the phone calls, so you wait for those calls to be completed with the help of await keywords. The key difference of waiting this time is, you are still free to do whatever you want because the phone calls are attended by call attendants.So if you get a call from your home this time around you will be able to take it. (analogous to application being responsive).
Once the calls are done, the manager lets you know that the calls are completed and you go ahead and log in your diary. Now coming to logging of the calls, you first log both the calls which have come in and once they are completed you log in the total time spent on each call. The total duration spent by you in this case is close to 2.5 seconds which is the maximum of both calls because calls are handled in parallel and some overhead in communicating with the manager.
Thread started to run for 2.5 seconds
Thread started to run for 2 seconds
Stopwatch passed 2002 ms.
Stopwatch passed 2554 ms.
Console.WriteLine("\Multithreading:");
Then you log "Multithreading:" into the diary
Calling method 'RunSecondsThreaded()'
RunSecondsThreaded(2.5); RunSecondsThreaded(2);
And finally you and your manager have a small fight and he leaves the organization. However you do not want to take the calls because you have other important tasks to take care of. So you hire a call attendant when a phone call comes in and have the work done for you. You do it two times because two calls have come by. Meanwhile you are again free to do other tasks like if you get a phone call from your home you can attend it.
Now coming to logging of the calls. You do not log the calls this time around into the diary. The call attendants do it on your behalf. The work done by you is just hiring the call attendants. Since calls have come in almost at the same time, the total time spent is 2.5 seconds plus some additional time for hiring.
Thread started to run for 2.5 seconds
Thread started to run for 2 seconds
Stopwatch passed 2000 ms.
Stopwatch passed 2501 ms.
Hope it helps in resolving your confusion

Using Task.Yield to overcome ThreadPool starvation while implementing producer/consumer pattern

Answering the question: Task.Yield - real usages?
I proposed to use Task.Yield allowing a pool thread to be reused by other tasks. In such pattern:
CancellationTokenSource cts;
void Start()
{
cts = new CancellationTokenSource();
// run async operation
var task = Task.Run(() => SomeWork(cts.Token), cts.Token);
// wait for completion
// after the completion handle the result/ cancellation/ errors
}
async Task<int> SomeWork(CancellationToken cancellationToken)
{
int result = 0;
bool loopAgain = true;
while (loopAgain)
{
// do something ... means a substantial work or a micro batch here - not processing a single byte
loopAgain = /* check for loop end && */ cancellationToken.IsCancellationRequested;
if (loopAgain) {
// reschedule the task to the threadpool and free this thread for other waiting tasks
await Task.Yield();
}
}
cancellationToken.ThrowIfCancellationRequested();
return result;
}
void Cancel()
{
// request cancelation
cts.Cancel();
}
But one user wrote
I don't think using Task.Yield to overcome ThreadPool starvation while
implementing producer/consumer pattern is a good idea. I suggest you
ask a separate question if you want to go into details as to why.
Anybody knows, why is not a good idea?
There are some good points left in the comments to your question. Being the user you quoted, I'd just like to sum it up: use the right tool for the job.
Using ThreadPool doesn't feel like the right tool for executing multiple continuous CPU-bound tasks, even if you try to organize some cooperative execution by turning them into state machines which yield CPU time to each other with await Task.Yield(). Thread switching is rather expensive; by doing await Task.Yield() on a tight loop you add a significant overhead. Besides, you should never take over the whole ThreadPool, as the .NET framework (and the underlying OS process) may need it for other things. On a related note, TPL even has the TaskCreationOptions.LongRunning option that requests to not run the task on a ThreadPool thread (rather, it creates a normal thread with new Thread() behind the scene).
That said, using a custom TaskScheduler with limited parallelism on some dedicated, out-of-pool threads with thread affinity for individual long-running tasks might be a different thing. At least, await continuations would be posted on the same thread, which should help reducing the switching overhead. This reminds me of a different problem I was trying to solve a while ago with ThreadAffinityTaskScheduler.
Still, depending on a particular scenario, it's usually better to use an existing well-established and tested tool. To name a few: Parallel Class, TPL Dataflow, System.Threading.Channels, Reactive Extensions.
There is also a whole range of existing industrial-strength solutions to deal with Publish-Subscribe pattern (RabbitMQ, PubNub, Redis, Azure Service Bus, Firebase Cloud Messaging (FCM), Amazon Simple Queue Service (SQS) etc).
After a bit of debating on the issue with other users - who are worried about the context switching and its influence on the performance.
I see what they are worried about.
But I meant: do something ... inside the loop to be a substantial task - usually in the form of a message handler which reads a message from the queue and processes it. The message handlers are usually user defined and the message bus executes them using some sort of dispatcher. The user can implement a handler which executes synchronously (nobody knows what the user will do), and without Task.Yield that will block the thread to process those synchronous tasks in a loop.
Not to be empty worded i added tests to github: https://github.com/BBGONE/TestThreadAffinity
They compare the ThreadAffinityTaskScheduler, .NET ThreadScheduler with BlockingCollection and .NET ThreadScheduler with Threading.Channels.
The tests show that for Ultra Short jobs the performance degradation is
around 15%. To use the Task.Yield without the performance degradation (even small) - it is not to use extremely short tasks and if the task is too short then combine shorter tasks into a bigger batch.
[The price of context switch] = [context switch duration] / ([job duration]+[context switch duration]).
In that case the influence of the switching the tasks is negligible on the performance. But it adds a better task cooperation and responsiveness of the system.
For long running tasks it is better to use a custom Scheduler which executes tasks on its own dedicated thread pool - (like the WorkStealingTaskScheduler).
For the mixed jobs - which can contain different parts - short running CPU bound, asynchronous and long running code parts. It is better to split the task into subtasks.
private async Task HandleLongRunMessage(TestMessage message, CancellationToken token = default(CancellationToken))
{
// SHORT SYNCHRONOUS TASK - execute as is on the default thread (from thread pool)
CPU_TASK(message, 50);
// IO BOUND ASYNCH TASK - used as is
await Task.Delay(50);
// BUT WRAP the LONG SYNCHRONOUS TASK inside the Task
// which is scheduled on the custom thread pool
// (to save threadpool threads)
await Task.Factory.StartNew(() => {
CPU_TASK(message, 100000);
}, token, TaskCreationOptions.DenyChildAttach, _workStealingTaskScheduler);
}

How to make two Tasks run with an even distribution of cpu time

They start out even, but eventually the processTasks never gets hits.
Originally I had this as two threads when the tasks were simple. Someone suggested async/await tasks and being new to c# I had no reason to doubt them.
Task monitorTasks= new Task (monitor.start );
Task processTasks= new Task( () => processor.process(ref param, param2) );
monitorTasks.Start();
processTasks.Start();
await processTasks;
Have I executed this wrong? Is my problem inevitable while running two tasks? Should they be threads? How to avoid.
edit
To clarify. The tasks are never intended to end. They will always be processing and monitoring while triggering events that notify watchers of monitor outputs or processor outputs.
If you await on a Task.WhenAll then it will wait until all tasks have been processed
await Task.WhenAll(monitorTasks, processTasks)
https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.whenall(v=vs.110).aspx
Task.WaitAll blocks the current thread until everything has completed.
Task.WhenAll returns a task which represents the action of waiting until everything has completed.
Task.WhenAll Method
Creates a task that will complete when all of the Task objects in an
enumerable collection have completed.
Task.WaitAll Method
Waits for all of the provided Task objects to complete execution.
If you want to block wait on started tasks (which is seemingly what you want)
Task monitorTasks= new Task (monitor.start );
Task processTasks= new Task( () => processor.process(ref param, param2) );
monitorTasks.Start();
processTasks.Start();
Task.WaitAll(new Task[]{monitorTasks,processTasks})
If you are using async await, see Asynchronous programming with async and await
Then you could do something like this
var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();
await Task.WhenAll(task1, task2);
I couldn't get tasks to run evenly.
The monitor task was getting constantly flooded whereas the processor task was getting tasks less frequently, which is when I suspect the monitor task took over.
Since no one could help me,
My solution was to turn them back into threads and set the priority of the threads.Lower than normal for the monitor task, and higher than normal for the processor task.
This seems to have solved my problem.

Task continuation parallel execution with async/await

In the context of a console application making use of async/await constructs, I would like to know if it's possible for "continuations" to run in parallel on multiple threads on different CPUs.
I think this is the case, as continuations are posted on the default task scheduler (no SynchronizationContext in console app), which is the thread pool.
I know that async/await construct do not construct any additional thread. Still there should be at least one thread constructed per CPU by the thread pool, and therefore if continuations are posted on the thread pool, it could schedule task continuations in parrallel on different CPUs ... that's what I thought, but for some reason I got really confused yesterday regarding this and I am not so sure anymore.
Here is some simple code :
public class AsyncTest
{
int i;
public async Task DoOpAsync()
{
await SomeOperationAsync();
// Does the following code continuation can run
// in parrallel ?
i++;
// some other continuation code ....
}
public void Start()
{
for (int i=0; i<1000; i++)
{ var _ = DoOpAsync(); } // dummy variable to bypass warning
}
}
SomeOperationAsync does not create any thread in itself, and let's say for the sake of the example that it just sends some request asynchronously relying on I/O completion port so not blocking any thread at all.
Now, if I call Start method which will issue 1000 async operations, is it possible for the continuation code of the async method (after the await) to be run in parallel on different CPU threads ? i.e do I need to take care of thread synchronization in this case and synchronize access to field "i" ?
Yes, you should put thread synchronization logic around i++ because it is possible that multiple threads would be executing code after await at the same time.
As a result of your for loop, number of Tasks will be created. These Tasks will be executed on different Thread Pool threads. Once these Tasks are completed the continuation i.e. the code after the await, will be executed again on different Thread Pool threads. This makes it possible that multiple threads would be doing i++ at the same time
Your understanding is correct: in Console applications, by default continuations will be scheduled to the thread pool due to the default SynchronizationContext.
Each async method does start synchronously, so your for loop will execute the beginning of DoOpAsync on the same thread. Assuming that SomeOperationAsync returns an incomplete Task, the continuations will be scheduled on the thread pool.
So each of the invocations of DoOpAsync may continue in parallel.

Categories