I want some clarity on this. I know that Task.Delay will internally use a Timer and it is obviously task-based (awaitable), whereas Thread.Sleep will cause the thread to be blocked. However, does calling .Wait on the task cause the thread to be blocked?
If not, one would assume that Task.Delay(2500).Wait() is better than Thread.Sleep(2500). This is slightly different that the SO question/answer here as I'm calling .Wait().
Using Wait on an uncompleted task is indeed blocking the thread until the task completes.
Using Thread.Sleep is clearer since you're explicitly blocking a thread instead of implicitly blocking on a task.
The only way using Task.Delay is preferable is that it allows using a CancellationToken so you can cancel the block if you like to.
Thread.Sleep(...) creates an event to wake you up in X millisec, then puts your Thread to sleep... in X millisec, the event wakes you up.
Task.Delay(...).Wait() creates an event to start a Task in X millisec, then puts your Thread to sleep until the Task is done (with Wait)... in X millisec, the event starts the Task which ends immediately and then wakes you up.
Basically, they are both very similar. The only difference is if you want to wake up early from another Thread, you won't hit the same method.
Related
I'm trying to wrap my head around control flow in C# when using async, Task, and await.
I understand how promises work, and that the returned Task<> from an async method will eventually contain the result of a computation/IO/whatever.
I think I understand that if you explicitly wait for that Task, then the current thread blocks until the Task is complete. I also think that means that the code in the async method that returns a Task will be running on a thread in a thread pool.
What I don't understand is what happens if I don't "await" the Task returned by an asynchronous method. It seems to me that the continuation is executed on the original thread that calls the async method, but I have no idea how control can return to that thread.
Here's an example. Here's I'm using UniTask which is basically Tasks for Unity:
public async UniTask ConnectAsync(Connection connection)
{
Debug.Log(Thread.CurrentThread.Name); -> this prints "Main Thread"
// Close Any Old Connections
await DisconnectAsync();
// Default Address
if (string.IsNullOrEmpty(connection.Address)) { connection.Address = "localhost:6379"; }
// Connect
ConfigurationOptions config = new()
{
EndPoints =
{
{ connection.Address, connection.Port },
},
User = connection.Username,
Password = connection.Password,
};
m_Connection = await ConnectionMultiplexer.ConnectAsync(config);
// Create Graph Client
m_Graph = new(m_Connection.GetDatabase());
// Notify
await Editor.Controller.OnConnect();
Debug.Log(Thread.CurrentThread.Name); -> this prints "Main Thread"
}
If I call this method, and then neglect to await the returned Task (UniTask), both Debug.Log() show that execution is happening on the "Main Thread" (i.e. the UI thread).
How is it that without awaiting this Task, the Main Thread is able to return to this continuation? Does C# wait until the thread is in the Suspended/WaitSleepJoin state? I'm not aware of any code putting the UI thread to sleep so I'm not sure about that. I'm certainly not putting the UI to sleep.
EDIT: I believe the chosen answer basically answered the question in the final sentence:
"Your code just needs to return to the main loop to allow the
continuation to run."
In other words, there's a loop somewhere deep in the bowels of (Unity in this case) and if the UI thread gets there, then it takes the opportunity to continue any pending tasks. (Please correct me in a comment if this is wrong and I'll update accordingly).
Incidentally, these links were very informative:
https://blog.stephencleary.com/2013/11/there-is-no-thread.html
https://www.ncameron.org/blog/async-io-fundamentals/
https://devblogs.microsoft.com/pfxteam/executioncontext-vs-synchronizationcontext/
https://blog.stephencleary.com/2012/02/async-and-await.html
What I don't understand is what happens if I don't "await" the Task returned by an asynchronous method. It seems to me that the continuation is executed on the original thread that calls the async method, but I have no idea how control can return to that thread.
As I describe on my blog, each await (by default) captures a "context", which is SynchronizationContext.Current or TaskScheduler.Current. In this particular case, the UI context is captured and used to resume the async method (i.e., execute the continuation).
How is it that without awaiting this Task, the Main Thread is able to return to this continuation? Does C# wait until the thread is in the Suspended/WaitSleepJoin state?
It has to do with contexts, not threads. The UI context schedules work by posting to the main UI message queue. So the continuation is run when the UI thread processes its message queue; it doesn't have anything to do with thread states.
I'm not aware of any code putting the UI thread to sleep so I'm not sure about that. I'm certainly not putting the UI to sleep.
Your code just needs to return to the main loop to allow the continuation to run.
I understand how promises work
Good, then we can stop right there. Tasks are nothing but compiler syntactic sugar over a promise. In fact, when JavaScript copied the await/async keywords from C#, they got implemented over the native Promise object.
Now, for the remainder of this I'm going to assume that you don't know how promises work. Think of it as getting called out on your promise bluff on your CV.
There's three parts to an async method:
The "synchronous" part. This is what will run when you simply call your async function, awaiting it or not, and is everything before the first await in your function. In your function this is the Debug.Log call and the synchronous part of DisconnectAsync.
The "asynchronous" part, the tail of your function. This gets stored as a lambda and it captures all necessary variables on creation. This gets called after #1 and when "done" it returns the Task object from your function. When the task is fully completed, the task is set as completed. Note that this can be recursive if you have multiple tails inside your tail.
All the magic of Task. For example, Task.WhenAll instantiates mutexes in your Task and then waits on them for completion. This makes Task technically disposable, and thus a memory and OS handle leak if you don't dispose every single task you create. await itself is handled through TaskCompletionSource, and you get just the task it manages. Things like that.
Note that nowhere in this did I mention threads. Tasks are to threads like what cats are to doctors. They both exist, some interact, but you have to be pretty insane to say cats are made to work only with doctors. Instead, tasks work on contexts. Thread pools are one default context. Another is single threaded contexts.
That's right, you can easily have async code run on a single thread, which is perfect for GUI in a single threaded render loop-driven game. You create a dialog, await its showing and get a result, all without any additional threads. You start an animation and await its completion, all without any threads.
This question already has answers here:
When to use Task.Delay, when to use Thread.Sleep?
(10 answers)
Closed 3 years ago.
I was wondering if there is any problem if i want to pause a thread for a defined period of time at every iteration ( i am running a continous loop).
My first choice was using Task.Delay but i do not know if there could be any issues.Should i just go for Thread.Sleep or EventWaitHandle ?
class UpdateThread {
private Thread thread;
Fabric.Client client;
public UpdateThread(Fabric.Client client) {
}
public void Run() {
thread = new Thread(new ThreadStart(async()=>await UpdateAsync()));
}
public async Task UpdateAsync() {
while (true) {
await Task.Delay(Constants.REFRESH_INTERVAL);
}
}
}
What are the downsides to the above mentioned methods ?
P.S: This thread is running alongside a Windows Forms application (thread)
There is a potential problem with the ThreadStart delegate that you pass to the Thread's constructor, which is defined as public delegate void ThreadStart(). The fact that you provide an async void lambda for it makes it a fire-and-forget call. I.e., it's asynchronous but it doesn't return a Task to observe for result or exceptions.
Your new thread will most likely end as soon as the execution flow inside it hits the first await something, be it await Task.Delay or anything else. So, technically, you're not pausing a thread here. The logical execution after that await will continue on a random thread pool thread, which will most likely be different from the thread you initially created.
You'd be better off just using Task.Run instead of new Thread. The former has an override for async Task lambdas, which you should normally be using instead of async void anyway. Thus, you could pass your UpdateAsync directly to Task.Run and have the proper exception propagation logic for async methods.
If for some reason you still want to stick with new Thread and pass an async void lambda to it, make sure to observe all exception thrown by UpdateAsync. Otherwise, they will be thrown "out-of-band" on a random pool thread, see the above link for more details. Also note, creating a new thread (and then almost instantly ending it) is a rather expensive runtime operation. OTOH, when using Task.Run, you normally just borrow/return an existing thread from/to thread pool, which is much faster.
That said, in this particular case you may as well just be using Thread.Sleep instead of async methods and Task.Delay, to avoid having to deal with asynchrony and thread switching at all. It's a client-side WinForms application where you normally don't care (to a reasonably extent) about scaling, i.e., the number of busy or blocked threads.
In this case you should use Task.Delay, because Thread.Sleep would send a Thread from the .NET ThreadPool to sleep and that is most likely not what you want. You are also mixing lower-level Thread with higher-level Task. You don't need to start a new thread. It is enough to just call UpdateAsync() without calling Wait() or similar.
Use Thread.Sleep when you want to block the current thread.
Use Task.Delay when you want a logical delay without blocking the current thread.
Source
I prefer handling such cases with a Thread.Sleep cause it's lower level and more effectively in my head, but it's just a personal thing.
The context for this question is a WPF application. WPF applications use a DispatcherSynchronizationContext.
If I have a button in my application that invokes the Button_Click handler method and I want to ensure all the code in that function is only ever executed by one thread I would wrap it in a semaphore as shown? But what I dont understand is how this works.
Assuming the button was clicked, we would hit WaitAsync() which returns a task that completes when the semaphore is entered, so I guess immediately? Then we would hit await GetLengthAsync() which would bounce us back out to the wpf message loop. Assuming 10 seconds goes by and the button is clicked again, then we would enter the Button_Click method again and hit WaitAsync(), which returns a task that completes when we enter the semaphore, and we cant enter the semaphore so we bounce back out to the message loop? is that how it works?
MAIN QUESTION -
Both times we hit WaitAsync() we are on the same thread, and our semaphore limits the concurrency to only allow one thread to execute that block of code at a time but it wont allow our same thread to enter that code either? the semaphore obviously cant be obtained by say some other threads like thread4 or thread5, but it also cant be obtained even by our same thread again? Any Clarification would be greatly appreciated.
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1,1);
public async void Button_Click(object sender, EventArgs args)
{
await semaphoreSlim.WaitAsync();
try
{
// GetLengthAsync takes 40 seconds to complete
int length = await GetLengthAsync();
// LongComputeFunc takes 30 seconds to complete
int aggregate = LongComputeFunc(length);
}
finally
{
semaphoreSlim.Release();
}
}
Assuming the button was clicked, we would hit WaitAsync() which returns a task that completes when the semaphore is entered, so I guess immediately? Then we would hit await GetLengthAsync() which would bounce us back out to the wpf message loop.
Yes, and yes.
Assuming 10 seconds goes by and the button is clicked again, then we would enter the Button_Click method again and hit WaitAsync(), which returns a task that completes when we enter the semaphore, and we cant enter the semaphore so we bounce back out to the message loop? is that how it works?
Yes.
MAIN QUESTION - Both times we hit WaitAsync() we are on the same thread, and our semaphore limits the concurrency to only allow one thread to execute that block of code at a time but it wont allow our same thread to enter that code either? the semaphore obviously cant be obtained by say some other threads like thread4 or thread5, but it also cant be obtained even by our same thread again?
That is correct. Some synchronous coordination primitives do have the capability to allow recursive locks (e.g., Monitor), but others do not (e.g., Mutex). However, it is unnatural for asynchronous coordination primitives to support recursive locks. (Personally, I'm against recursive locking in general). Synchronous coordination primitives can get away with recursion because there is the notion of "a thread" that "owns" the lock. For asynchronous coordination primitives, there is no notion of a thread owning the lock; rather, a "block of code" owns the lock.
So, that's a long-winded way of saying that SemaphoreSlim.WaitAsync is not recursive (and shouldn't be).
Now, whether this is a good UX design or not is a different question. As noted in the comments, it's more common for UI to disable a button that represents a long-running operation if you only want one to be started at a time. That said, you could use the SemaphoreSlim approach if you want to allow the user to queue up more than one long-running operation. In this case, the SemaphoreSlim acts like a sort of implicit queue for your code.
I want to run a function on an interval, inside of a Task. Something like,
Task t = Task.Factory.StartNew(() => {
while (notCanceled()) {
doSomething();
Thread.Sleep(interval);
}
});
Is it a bad idea to use Thread.Sleep() here? The task is long running, and the sleep time may also be very long (minutes, maybe even hours).
One alternative is to use either System.Timers.Timer or System.Threading.Timer. However both of these would cause an additional thread to spawn (the Elapsed events occur on a new threadpool thread). So for every repeating task, there would be 2 threads instead of 1. The Task is already asynchronous so I'd prefer not to complicate things in this way.
Yet another way that behaves similarly is to use ManualResetEvent,
ManualResetEvent m = new ManualResetEvent(false);
void sleep(int milliseconds)
{
m.WaitOne(milliseconds);
}
Since m.Set() would never be called, this would always wait for the right amount of time, and also be single threaded. Does this have any significant advantage over Thread.Sleep()?
Wondering what the best practice would be here.
Thoughts?
If you're using C# 5.0 you can use:
while(notCanceled())
{
doSomething();
await Task.Delay(interval);
}
If you're using an earlier version your best bet is probably to use a Timer.
Both of the code samples you showed, involving either Thread.Sleep or a ManualResetEvent are blocking the current thread for that duration, which means your code is tying up a thread which can't do anything else until your task is canceled. You don't want to do that. If you use a timer, or the await code mentioned above, you will end up not blocking any thread at all while waiting, and then use up a thread pool's time only when you have productive work to be doing.
Yes, it is a very bad idea to use sleep in a loop like that.
You have a faulty understanding of timers. Creating a timer does not create a new thread. The timer sets an operating system trigger that, when the time elapses, spawns a threadpool thread. So if you write:
System.Threading.Timer myTimer =
new Timer(DoStuff, null,
TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
The only time there will be another thread is when the handler method (DoStuff) is executing.
There'd be no reason to have the task if everything it does is handled by your DoStuff method. If you want to cancel it, just dispose the timer.
I strongly recommend, by the way, that you not use System.Timers.Timer. In short, it squashes exceptions, which hides bugs. See Swallowing exceptions is hiding bugs.
I have been looking at this article about using tasks in C#, and I was wandering if someone could clear something up for me? Doesn't calling wait on a task defeat the purpose of creating the task, because wouldn't it freeze up the main thread anyway because now the main thread has to wait for task to finish. I'm assuming however that wait won't be called straight away, so now when would you call it or how else would you know when to dispose of the task.
Assuming we have a simple case like this:
void MyFunction()
{
Task t = Task.Factory.StartNew(() => { System.Threading.Thread.Sleep(5000); });
}
if you call t.Wait() straight away the main thread will still wait 5 seconds before being able to do anything else, which kinda defeats the purpose from the users point of view, they wont be able to do anything for 5 seconds. How would you know that after 5 seconds that task has been completed? And you can dispose of t? What is a correct way of handling that? Sorry if the question is being really naive :(
Thanks All :D
You would probably never call Wait on a single task like this from a UI thread - as you said the whole point is not blocking the UI thread. In fact waiting on any task at all from the UI thread would be a problem.
Waiting for task completion might however be useful to synchronize multiple tasks and return a combined result - e.g. imagine two tasks that execute a hotel query on priceline and expedia concurrently and the thread that spawned both tasks (e.g. a background thread) waiting on the outcome of both and combining the results to order the available hotels on both sites by price.
The final result of the queries can then be dispatched back to the UI thread, typically by executing a callback or raising an event.