C# Threads, Tasks, Multi-threading & UI Cross-threading

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] so in this video we're going to talk about threads multi-threading cross threading of your well everything accepts specifically tasks because they have a whole new thing that are built on top of threads and you know the thread pool and things so this is basically an introduction into the world of how threads work how things in your code can run in parallel with each other and how you then bring them back into sync and all the the little bits around that so we're gonna dive straight in we're gonna make a console up a dotnet core console up and start with the basics so the first question I guess is what war are threads so threads are basically you can think of them as sort of processing lanes for information that your application wants to process so in this function main here and we have a hello world and we put a few of those in and then at the end we have their console dot readline you can think of this as a set of instructions that happen one after the other so in series with each other so you come into main then you hit line 8 and hit line 9 you hit line 10 and so on and this is the structure of your code so if you had to put a breakpoint in here and run your code then what you'll see is that you get into your code you hit this line and you press f10 to step over and this is the next line and then this is the next line and it's a sequential order it's obvious of what happens and then you get to the pointing code where your program now is waiting future press enter and then it go to the next line so that's almost you can think of and it is in this instance a single thread it's a single piece of information that happens line after line and that's easy to understanding code it's the flow it's how things work that's everybody understands that you understands programming that's kind of what's happening your code is expected that line 10 will run after line 9 now that's okay until you get to more advanced applications and you want to do things in parallel so say you're downloading a picture from the website or from a website or other and you want to show in some UI like a loading progress spinner or at least the tax load you don't want to freeze up your entire application while it's downloading so in order to do that you need to spin up a new thread and to download the image on a new thread say and then once it's done you come back and you update your UI you could be doing a bunch of processing that you want to say you're processing while you're working but again without hanging your application so in a console application it's pretty much straightforward there's a main entry point and there's a single thread that runs here and when this specific threat is done the application closes so if we were to remove console dot read line this will simply run out put all the information and closings thinking without has seen the the application so we just for a breakpoint loser's already ran enclosed it'll spin up run and there's the code and then we press f5 because it's only stopped do trial breakpoint and it closes so this is the thread that's run and closed so in order to I guess get familiar with things happening alongside this will just basically create a thread and start showing you what happens so we have the hello world and that now runs and when we run it simply runs and waits for you know us to then press Enter so if we wanted to now do something while this is waiting here you will we can now start a new thread so to do that type new thread control dot to open up the suggestions and include the system threading namespace and then then here we do our function or our action in this case and now we're in a new threat so here we obviously have to also start the thread at the end so guru start this will now spin up a new thread and then here this will basically start running go to here and when it gets here it finishes so right now that's a blank thread that will do nothing but in here we can at least do console.writeline to show it's running as a new thread and if we run that code you'll see now that it says hello world and then this is a new thread so we've done things that look like it's the same as doing it one line after the other but that's not what's gone on here this is a completely separate thread so what we can say is this new thread one copy and paste just for good demo purposes here you can do one two three and typically this is not really going to show you much because everything seems to happen in order due to just the way you know this is happening but one thing you can do is say do a thread sleep and put the thread to sleep for a second and what you'll now find is that all three of them wait for a second at the same time and if I could get this thing to keep opening in the right window just it's adamant it wants to open in the wrong window every single time so let me just pause this application here until I press on to just so I can show you that before it spins up so I press enter now you get hello world and then a second later all three fire so what's happening is this threads running this threads running and this threads running all of them are running at the same time so they all wait for one second between them and then they all output their information whereas if you were to remove the threads and you were just to run everything in a single thread in a single series line if you will the behavior will be different so this time you'll get hello world and the first one the second one the third one because they're not running and you threw out they're all run in the same thread so this now follows a straight order so what we've done here by introducing threads is that you can think of these as completely separate pieces of work that are being run all at the same time if you will so the CPU splits his workload up between all the threads in the application and assigns you know micro chunks of time for these threats to be run so they'll all kind of run in parallel you can think of them all happening at the same point in time and then that's you know that outputs the information so in order to just simply demonstrate for one that a point I want to get across is that this this thread has you enter the application runs and then ends and will basically end as soon as you know it's finished shall we enter there and run and then we press enter it'll say hallo world and then it will end and the reason it also didn't end by the way as soon as it got to here which it will off have we hit here and run the application you can see we've got there and it waits for these threads and there's a reason for that and that's because this thread or new threads by default are being created as foreground thread so we're getting a little bit ahead of ourselves in terms of understanding what that means if you will so we'll go back step in the first point on to prove as that simply we were to delete the threads and we just remove that as we demonstrated this will now instantly close as soon as it's displayed that word so as soon as this thread this function here gets that the end and is done then it will basically close so the way we can kind of show that is instead we'll create a task completion short source new task completion source and we will now instead of doing console dot readline we would do task dot result and we'll simply wait for the result so now I'll hit this line and effectively we'll never get to this line as this will show opens or press ENTER and we get this line and now we never actually the program will never end because it's waiting for task completed completion that never completes so we've effectively halted our read here view well from ever completing and now what we'll do we'll start a new task or rather the new thread and inside here we will simply say thread not sleep so let's wait for say two seconds and then complete the [Music] the awaited task completion sauce so now what will happen is once that runs and we press ENTER I'll wait for two seconds and then it should have actually finished which it hasn't what's been on there oh I'm at the start so we walk made the new the red we need to actually start with red let's try that again so it starts off palette world and then two seconds later it closes because this is finished sleeping it's that's the result here and now this can continue so the the real demonstration here and the point here is that your console application in this case and the same for all applications would WPF applications any other application they have an entry point which is typically a static void main that comes in and simply runs the single thread that it's on until the thread ends and then once that main threads ended the application closes so that's how it typically works there is an exception to that and as you've seen we can simply hold the application and we delete that so now the application should close straightaway as it normally does but instead we tell it to wait say for 10 seconds or that's a second it's not gonna be obvious if we tell it to wait for 10 seconds now if we drag this in and press Enter we have hello world you can see even though it should have finished this thread that's running separately keeps the whole application alive so 10 seconds later they should then finally close so the reason for that is that when we come in to me and we hit here so I'll show you the code I've got down here the threads and tasks window so we don't see those you click debug windows and then you've got threads down here and tasks which we'll talk about near the end of the video so the third video you can see we've come in and this is a main thread and there's the process ID and the managed IV of zero so we have the thread and as we step through you can see that's the thread wrong that's the gray thread that we're running now with this arrow indicating that that's where we will break pointed to here so we're on this single thread and we now wait for me to press enter step over and then at this point we're going to create a new thread and start it and then we'll hit inside the thread here so we press f10 you can see the threads now been created if you will a new thread and it should start and we've hit the very next line you can see there's no thread yet we step over though and you'll see now it's started this thread this one's now finished but we've never got a worker thread and this has got an ID and you can also see the main thread has now been destroyed so even though the main thread that we entered in on has now since finished and completely gone it's been destroyed we've got a worker thread active and because this thread is alive the application won't end and more importantly because this thread is what's called the foreground thread the application won't end so now we've got this new thread and you can see the arrow is now pointing to this thread which means this one's you know the thing that's active and running and it's gonna wait for those 10 seconds for that delay and then when it finishes it will obviously end the application which I don't then we can hit any breakpoints at that point it's basically the clothes for the application so 10 seconds later this will simply kill the the debug session so if we wanted to show and really the only important thing there with a foreground thread which is created by default is that foreground threads the only difference when a foreground and background thread is that foreground threads will keep the application alive so we're able to change this thread to a foreground a background thread instead of the default Sybil's any overloads anyway we can make use of and if not we'll just simply change it here is background equals true and then we start it so a bit of a weird looking way of editing the object on there more typically you do from the night bar so that equals new thread you put your action inside here but those should work and then you do thread dot its background equals true and thread starts that's kinda what I'm doing here I hope I'm doing all that in one line it just looks a bit strange on the syntax but now it's a background thread what you should notice is when we bring this in and we press ENTER it says hello world and effectively dies straight away and that's because this is now a background thread which means it shouldn't keep the app alive this simply gets run and if the app terminates it's simply killed it's it's it's done an instant thread kill if you will so that's the difference between foreground and background and that's kind of the explanation of how your applications working in terms of threads it will come into this main thread you'll have a thread and that will be a foreground threaten each year only thread that exists and then the application will simply run all foreground threads which in typical circumstances is the only one so as soon as Esther gets to the end it would exit unless in the course of this thread you make more threads that are foreground and then it will wait for all them to complete each thread that you have will run parallel to each other so if we had a thousand threads and all the threads were sleeping for one second and then when they finished we were done and I won't do a thousand does a black stream but let's say innumerable dot range let's do ten threads and we just cast them to a list and then for each and then in there so all were doing here is making a effectively another way of in UML got Rangers to do for bar I equals zero eyes less in turn i + + tada and then you put your stuff in there but to me this is a bit old school now for thread I know this is not super efficient in terms of doing this but efficiency isn't a concern in this one whereas s Tamizh a bit more readable so in a range of 0 to 10 which in terms of the the count so from 0 to 9 in terms of what you'll get or ultimately if you start with 0 and then put number here this is how many times you'll iterate so we have a range of 10 that we do something for each item so to me this is more legible in code but it's just a for each loop where you know speed isn't if a concern in this example it definitely isn't so should a quick and quick and nice way of making a flexible for each loop where you don't really care about speed so slightly off topic there but that's all I'm doing here so now we'll hit this ten times will run ten threads all of them or wait a second and then the application should close so what should happen is once this is created all ten the application should basically close so in roughly one second after pressing enter the application should close and there is a bit of set-up cost in terms of creating threads it won't quite be that quick but we press Enter and then you can see it's gone in approximately one second what it hasn't done is it certainly hasn't waited for 10 threads waiting a second each so you can see that these are all running and if we put a breakpoint you'll also see that the int we hit enter will have one thread running 2/3 1/3 thread 1 and you can see all the threads here being created so you can see now we have all these threads some of them have since died since I've run it because they're all lobster Beamer at the same time but you can see by the ID we have a bunch of threads running so you can see them all running there the other thing we can also do is say console dot write line and we can say thread and we'll do red dot dot current thread what is it sorry not yeah let up current thread dot ID manage thread ID there we go so thread ID I know we just simply I'll put the thread ID it doesn't really matter and we could also yes I'll do for now so we run that and then we are also not gonna see oh you'll see that for a second which so you can see I've just grabbed hold no in fact let's just do truck this at the end for the minute just so it stays open so you can see that output you can see there the ID of the threads is a new ID per one and then the course of doing this it's already God the IDS aren't necessarily starting from 1 all the time basically but you can see that each of these threads is a different thread and they've all run at the same time and then we can also do say thread indeed so then when we look at this you'll see the process of ten of them starting and ending so they've all they're all running and you can see the slightly out of sync number now to show that they are really running all on their own threads so this is all running parallel to each other so that's that's kind of the hopefully you'll understand that the basic principle is that when you start a new thread it runs in parallel with every other thread that is in the application and if you want to visualize that happening then obviously just put a breakpoint in sure the thread window here and then if you're going through and you seen threads being created you can sort of watch them watch them being created here and you can also see by this arrow which thread you currently enter you in thread number five here then you in seven then you in six then you're in eight so you can sort of visualize what's happening to your threads here and also the state and the line that they're on and then you can jump between them to debug to jump between any threads at any point in time now one of the points I'd like to also make at this stage is you might be thinking right I've got a lot of tasks that need to be done nowyou code wants to do a lot of things you know your applications running and maybe of you doing an application that's receiving all our data from a lot of places or does a lot of work and you first thought now might be or let's spin up a load of threads and on each thread we'll do different pieces of work and then they'll all run in parallel everything will work super quick and say you're doing file encryption or reading chunks from a file and doing something with them maybe you'll think of I'll split that up into twenty threads and then twenty threads will be read in different parts of the file asynchronously for whatever reason you're thinking of abusing multiple threads there's basically a warning don't create too many threads is the underlined sort of message so things in code and things running in parallel are not always motor time and not always quicker than simply having a single task that runs in a straight line if you will and that's because ultimately CPU only are so many true cause that can actually run in true parallel so on quad-core processors for instance you have four truly independent course and inside of they then have virtual cores that can go 64 128 can have a lot of virtual cores but ultimately for the most part and and how it was before you know multi-core computing come out a single core CPU only ever had one piece of information it could run for the whole system at any one point in time and it simply worked on a super-fast clock and allocated you know nanoseconds of work between every single process on the whole machine so at that point in time threads and multitasking were in no way quicker than doing everything in sort of a single process if you will but ultimately there are cases where multi-threading and doing things on you know separate in parallel processes gain speed but it's not usually the case and it rarely is the case more of the time when you want to use a thread or you want to do something in parallel it's not for speed related purposes it's more so in the most common reason why people probably watch this video and the common reason why people want to know about threads and anything to do with threading is the classic issue of when you have a UI so you really know windows forms application you and in WPF application whatever you're running people that aren't aware of threads or how things work will say try and do some some form of task or some form of computation or pull information from a database and all just be doing it there and then writing code so you'll click a button another one to read something from a database and what we'll find is that all of a sudden their entire UI is from God and they're not sure why and then read often they'll find out that it's because they're running worker processes if you will something that needs to do a task on the UI thread and then that's that's where it kind of leads into this discussion so don't really think of threads more along the lines of doing things quicker just because they happen in parallel it's more to spread the load and to you know allow things to happen is you need them to happen in code feel like you download an image from the web site or you download some data in your policy but while that's happening you want to be updating a spinner or you want to go off and query a few other places do some other things in code so it's not always for speed it's simply for having your code work without locking other you know work up if you will multiple things happen in a want that's was countable a warning with threads don't don't go creating like say as created that wasn't thredson and run s and you'll see that it will in order to create a thousand threads when I press ENTER you'll find that is not exactly quick that's that's pretty damn slow that's in fact that's very slow so you can see it's creating you know a thread probably taking 100 milliseconds to create so this is not a quick thing to to create a new thread and we'll move on to task shortly where tasks use what's called a thread pool and the thread pool and setup simply making a new thread you can use thread pool dot Q worker item and if we change the call to this instead of a new thread so we do Q worker item close that off and we want to wait callback so let's have a look at wait call back go in it's basically gonna accept an object so we just pass an object we don't care about it should be ok and we don't start it so now we use a thread pool to do the work for us and we press ENTER so I'm go to the and you can see now this is and you can see the ideas being reused so what's happening even though we've got a thousand threads being created it's it's reusing almost the same threads all the time and it's not necessarily spitting up a load of new threads we haven't got a continual increase so the thread pool is a different thing again that we'll talk about they'll queue up the work and make and reuse threads Trank cut down on some of that amount of work if you will but that's also got some issues and a lot of things to talk about there as well but a lot like you don't need to you don't need to really ever worry about the thread pool for you know what this video is about it's but what we're trying to cover here is the simple understanding of threads in general and there's we could talk for hours and hours and hours on on cross threading and thread pool you know recycling and calm and tasks and there's all kinds of topics but basically don't worry about them understand the core principles first and then learn what you need is the requirements pop up so hopefully we understand like say the principle of a thread them in a head we know that this code is running independently and in parallel with the other things so the next part I'd talk about them is gonna be well where this builds up to basically is now you've got past the demo state and you want to do something real now so in here we want to say download an image and then say that we've downloading an image so once I simply say we've downloaded an image and once we're done we want to continue so we don't want this line here so we do write line and we just say all done so this is where we want our application to end and in code you naturally want to do your piece of work and imagine this is a UI that I'm talking about here that you don't want to block this thread because this is the UI thread so in here we want to download some pictures all of our web clients equals new httpclient and we'll do say webclient dot get string we don't care it's not a string that's going to be a what we can just do first we can just do my website's call the home page and you can see if we do that once we finish this we want to then allow this bit to continue so if we were to say start in and download and then if we were to say done downloading and this is all example code to kind of show you the flow of where things are going so if we press enter you can see it says hello world all done start downloading and dawn whereas that's not the order we wrote the code in we've got hello world and then we want to start downloading and when it's finished say dawn on them and we're all done so what we want here is because this is as you know being running its own thread and this is the other you know the first thread the main thread what if at this point in code we want to wait for this piece of work to be done above so that's kind of where it will naturally evolve as you start using threads in your real code and you've got a bunch of work here there's gonna be some point in time on some thread or some call or some function you've got where okay this is now a function that says download an image but you want to await that call you want to say right once you've downloaded I want to continue what I'm doing and protest for that are waiting if you will you don't want to lock anything so we'll get onto tasks which will be perfect for that but the mini we want to stick with threads to kind of show that so what we could do is say well thread equals new thread and we don't start it we just get the thread and then the new threads start and that will do exactly the same it's just now that we have the access to the thread so in order for us to effectively lock and I mentioned you don't want to lock the thread that is at the point of why we'll move to tasks and remember this is just proving the point say we don't want this line to continue until this this piece of work is done you can do thread join so by joining on to the thread piece of work that's happening our common thread will simply a way to not move until this piece of work is done as this is just a really quick way of kind of showing it's a blocking call so you can see blocks the calling thread until the threat has done its work or been terminated basically so now by running this code what you'll see is we'll have sort of a similar affair as if this was all run in one statement and we've blocked it sweeper Santa hala world starting dawn and then hold on so it's this thread at this point is waited until this threads done and now this is not what you do because all you've done here by joining is simply it's a fact that we've done this you've removed that and you you've locked again so now we get the exact same response doing it this way we just you know not what you want but the point I'm trying to prove here is they'll be parts the timing code where you now you want to wait and not continue but what you don't want to do is block your actual thread so if all the work is happening on your thread then you don't want it to lock by doing this but in terms of a console application there's only one time that you've only got one piece of work happening in order anyway so it's hard to to show in this context so we will share it in a WPF application to show you the the principles of doing that and in a more real-world situation which is basically this with a button but I just wanted to show you a quick way to kind of you know join onto that thread of you were so let's just first touch on tasks in the console application before we move to the WPF application because once we go to WPF I'm gonna get some threads that appear for my stylus on my touchscreen and things like the ill milk the waters between me trying to show you the threads so as you know right now we have this thread we create it we start it and then we realign here so we've done this application and I attempt a a bit late and half hitting enter oh there we go so you see we've got a single thread and as we've shown as you step over you've created that thread but it doesn't appear till you start so once you start the thread then you see the work of thread so by directly creating a thread you get a thread here you get an ID and that's how it all works so bear that in mind and then now what we're gonna do because there's there's rarely a case where yet you want to use a thread this has just been shown to give you the idea of how to directly create a thread and majority of the time though what you want to use is simply a task so there's there's differences I'll show you the task and I'll kind of talk about why and when you'd potentially want to use a thread but this is not the normal thing you do so this federal go they've got this piece of info here and we're back to here so now what you'll see me doing on all of my videos is using tasks and all the task is now I'm really over simplifying it so don't take this for exact science of your this is not exactly what happens inside a task it's a lot more complicated than I make out but in order for you to understand what's happening and all you really need to care about is let's take a look at this example tasks run so a task now in terms of what it is and again oversimplified just for explanation you can see the task here you can see it's nascent results and it's disposable and you got all this info none of that you really have to bother about or care about you'll learn it as you need it so don't worry too much about that but in terms of what a task is when you do tasks top run and you've created a task and it's returning a task here all this is doing it again I keep stating this in case you know you think I'm gonna say all's it does but I'm talking simple terms here it's not what the real code does but you can think of it in this way so walls tasks top run is doing its queuing up a new thread pool docu worker item and then it's gonna pass in your piece of work but you want to do and also the thread pool that queue worker item is doing is this one thread pool per process so our entire application has one thread and that can be assigned and created it basically manages a bunch of threads so creating a thread is an expensive process so we do new thread that is an expensive process to generate a new thread so when we do new thread we are directly creating a new thread when we do thread pool cue work or item the thread pool keeps a bunch of threads open once they're created and when the work on that thread is finished is then recycled and reused without having to be remade from scratch so a thread pool is kind of a a slightly more efficient way of creating threads rapidly you know being created deleted created deleted without their overhead so the task Taran firstly it doesn't create a brand new thread it simply creates it picks a thread from the thread pool or rather the thread pool picks it a threat so all that means to you is that everyone to do tasks Taran a thousand times over and during the course of those thousand times as we showed in the last example things were waiting and finishing at different times and and then say this a fiftieth thread comes along but you know 20 of the other threads before it finished so you're gonna have a bunch these 20 threads or floating ready to be reassigned so the fiftieth thread will then pick up one of the old threads so it's kind of like a you think of it as recycling you're not always gonna be guaranteed a new thread number it's gonna recycle the threads so I can try and show you that before before we go into that detail that's at least just show you that running in terms of a task running instead of a thread so we enter the application and we drag this from every single time we press ENTER and you see we've got the main thread at normal then we have tasks top run and then we have all done so you can see now we've got two all done we haven't yet got inside of this task the first line but you can see we've already got a worker thread and then you can see the worker thread is the thread pool work queue so this is now getting ready and in the tasks window you can actually see it schedule the task to run so it's expecting to start so the difference here is this thread is part of the inner workings of the tasks run and then when we finally get inside of the task now this is our piece of work we want to run on a different thread you can see we're inside this anonymous method here which is on its own thread and we also have this worker thread here so we now have this task that's running on a thread a task ID of 1 and it's linked to this worker that he ultimately this is running on this worker thread so it looks slightly different in terms of keeping track of the threads and you get this new view here of a task that's running because tasks aren't always one-to-one mappings for your threads over here so as you've made a task there it's very similar to making a thread and you can see it all happens very similar but the key differences one as I mentioned it goes on the thread pool not not a direct creation and the second important thing is a task it's always created as a background thread which means that this will not wait for this to finish so when we create this from wrong if we were to remove the readable line and press Enter even though this is downloading this will just instantly exit so we can prove that as well by say having put in an async into this task and a waiting task out delay 10 seconds I never to run this application and press ENTER it instantly closes and that's because as we showed again in the previous example with a plain old thread it's a background thread so it's not gonna wait so we're now creating in this way and it's nicer and easier to do so it doesn't lick it too much right now but in order to know how have this a weight if you will wait for this piece of work to be done in a typical call when everything becomes asynchronous which is a bit outside of this video you know anything up here and then you await like this and you know wait that task to be completed so now this call here won't happen until this call he has done but the key important difference here is everything else it's on this thread that this you know action has been awaited on will continue to run in the background and everything else that uses up I can continue to work it doesn't doesn't block the call and effectively doesn't block the UI a bit on a UI thread it simply let's see carry on work and come back and there's a lot of code that goes on behind the scenes that allows that to happen and like say I'll do a separate video specifically on tasks because there's a lot to explain a lot if you want to dig deep into how it works but this video is purely to get you familiar with things simply happening in parallel and then really just familiar with what threads are how to at least look at them how to create them and how to start making you so guess of tasks Taran so I think that's enough in the console applications let me just copy this bit of code here and we'll go ahead and making you WPF application so let's just close the solution I'm just gonna restart and try and get this thing to open up on the same window as well sometimes they'll keep dragging it onto the the other window but let's give it a go first so new WPF up go ahead and create that and then we will just chalk in a button with a name so I can access it behind scenes my button content is hello and then in order to visualize this let me just first paste this in here just so I've got on my clipboard and let me just copy and paste some code I've written just a quick style in order to where are you one second there we go I just made a quick style so that when we run this WPF up now you'll be able to see the UI responding so I can kind of show you a UI deadlock so it keeps opening on that screen let's close that reopen and hopefully it remembers the window No okay let me just quickly kill this because couldn't drive me mad I mean should we start this visual studio and see if it will open on the right window okay that didn't work so for now for some reason gonna have to keep dragging this window up so there's WPF application you can see on how far we go yellow and when we remove your green so you can kinda see that's the responsiveness of the UI when we drag it around this happens so this is basically showing you the UI working and these interactions and the UI that happens the changing colors is on one thread and that is is what happens in WPF everything on the what's called the UI thread the main thread of the application there's only one thing that can ever update the UI so can ever change the call of or change the text or things like that it's a single threaded thing so that you know multiple things don't try and change the UI otherwise it won't know what state to be in so let me just add a quick click event to this just for demo and let's show you a deadlock so perfect example here now if we were to do something on here which here we go this is exactly what somebody will probably doing a hazard or a thousand times over we don't need the logs now but what we are gonna do is download my webpage if you want the home page to my website when we click the button and the important thing is here we are on at this point the UI thread and we can prove that as well by doing we put that back in and do debug instead debug dot write line on red and we'll simply print out what that were on and that will be red dot dot current thread dot managed ID and the HTTP client that seems to have disappeared so when we click this button now just down here if we go to the output and we clear that we should see when we click the bottom it tells us we're on thread 1 and more importantly you won't see much now have to restart because it speeds up after the first time you know the HTTP client will work quicker provide you can see when I'm hovering and now it's the color changes so if I click and now hover in and out see how it just froze therefore split second so in order to kind of demonstrate that better let me just change this to download a twenty-something MEK file so we're gonna change this oil to point to my download are some SolidWorks files this is about 20 Meg so now this should take a lot longer to process I'm on superfast internet but still should be a lot longer so now the work we're gonna do is gonna take a while so when I click this button and now hover in and out that is not exactly doing what I wanted it to do I wanna gun we've got get string async so that the thing we've got here is not awaiting so what we're getting here is a task not the result so let me just first force the result that's what the issue were having so this is getting basically downloading the file so now if we click here you can see now the whole UI is hunk nothing's happening my mouse is not on there nothing's happening but when the file finishes downloading oh then all of a sudden it responds again so this is a UI deadlock this is when yeah you're hanging in the UI so I'll do that again and click you can see I can't even drag the window nothing happens and when he finishes that windows just going to jump and respond so you'll see that don't for there we go so that I'm sure you've seen that happen in your application before maybe not as long or maybe say you've made a login page to application and you press enter and then it goes from dos kind of a web call but it does it all synchronously it does it like this it simply goes off and tries to log you in and in the meantime while you're waiting for that the entire application the UI of the application is hung and the for that as simple as I mentioned all of the UI on WPF and wind forms and most UI's has to be on a single thread there's only one thread that can update the UI and because when you click a button that event comes in on that same thread so you're coming on thread one which is the main thread of the application this work then happens on that thread so then when you are hovering over the bottom the thing that's responsible for changing the color tries to change the color but the thread is busy doing this because this was done first so now that thread as a hundred percent consumed by working on this piece of information with a stick to work on so nothing else on that thread can be used so that's kind of where you get the initial requirement just to start doing multi-threading everywhere and as we mentioned you can do threads so you can start a new thread and simply do the work as we've done so you can do this and run and then now this work will still happen but while you'll find it is on thread will give you a different ID so if we clear down here to see and we click you can see the you are still works we hope so we need to start the thread I always forget to start with red so you click here clear this off you can see now on thread four but this is happily responding while it's downloading it's not hanging the UI thread at all so you can do the thread or you can do as I mentioned instead of threads we use tasks because the the more modern they're easier they were on the background by default you know this that's why we use them so we just cut that into a task it does exactly the same thing so that task automatically run at this time I'll queue up on a thread pool clear that so you can see easy click the bottom you can see everything still works perfectly fine now that's all good heaps all one issue you've now say this was logging into the application what's the first thing you do once you've logged into the application you then go right I've logged in now I'm done here so this is where you will be now let's say logged in you'd then say change page or you do something that makes the UI change so you'd either display a message box should update the UI so in order to show that we'll say ok then my button drop content equals lock them and before I run this because I know this will crash now press ctrl alt Ernie and make sure common language runtime is checked so we we get the exceptions court and if we run this application now again this is what thousands and thousands of people do and this is why they kind of start coming to multi-threading and what goes on we click that button everything's fine and responding and then when we finally finished downloading that file we should get this crashing it can't still be downloading joy maybe it silently crashed was out there we go Sony just finished downloading so now it's downloaded we try and log in and [Music] come on slow wow this is getting really slow there we go and you get this exception the calling thread cannot access this object because it is it don't mind different thread basically so what this is trying to tell you and this is again one of the most common exceptions is we can't edit this content item because the thing that created this content was thread number one never go to threads it's the main thread here so this thread is the owner and the main thread of the application and all UI is created on this threat and has to be in order to follow the STA standards so we can't edit this content unless we are on thread one and because we are on as the output shows the read for we get this exception and this is part of multi-threaded applications and being able to communicate with objects that are on and created on and live on different threads so because the UI object my button was created on one thread and we've jumped off onto another thread here and then tried to edit the contents we can't do that we first need to jump back to the thread that this object was on so you might ask well how to do that in this specific situation all of the UI objects in WPF so my button have we go to the tire you'll see this here's of type button base the bottom base is a Content control the content controls of control controls a framework element which is a UI element which is a visual which finally is dependency object which boils down to what's called the dispatcher object so this big long huge chain of objects all UI elements in WPF are part of this dispatcher object I'll do another video on explaining that in detail but the dispatcher is responsible for allowing other threads if you will it's a result of a few things but it's mainly responsible for allowing other threads to dispatch messages to the thread that this runs on so effectively allows us to jump back onto another thread and we need to jump on the thread that this button was created on and then we can do what we want so what that means is we could do one thing we could do my button dispatcher because that's part of the dispatcher object and then don't invoke and then we could do a normal action as we do all actions and then here we can now mess with my button or we like and this will not work so we were on this code sure this works and the reason this works is because again if we copy this into here to show what thread run will say now on thread and then we'll change content so we click this button run thread before we wait for their 20 I'll make download and then once it's done we should see down here finally I should have picked a smaller file I'll probably change that to just the web page now we know that the responsiveness isn't an issue when this is finally downloaded Internet must be in slow come on Internet's going really slow actually something's wrong there okay let me just kill that and make this quicker cuz this is taking forever there should not be super quick so we will have this work done we're on thread four and then now you can see now we're on thread one and it's also changed the text successfully this time too long do more so what we've done there is we've jumped between threads so we click that button again we'll get into here and then we'll get into here so we click the button you can see we start here we have this code is now running on thread for here showing by the arrow so we've run and you can see then here we've now jumped over to thread one we've managed to get back on the main thread so that's kind of the purpose of the dispatcher but all's we're doing here is managing to get back onto the previous thread so we can do that in many ways but kind of doesn't matter so because the main window is also a UI object as I mentioned and we're in the main window if we were to step into the main window and go to the window which is in a Content control which we know them bubbles down for control framework UI visual and then dependency and then finally dispatcher we already have a dispatcher because like I say every single UI element has a dispatcher object with music our dispatcher that dispatch will also be the same for every single UI because the whole application only have one UI thread so what that means is we don't need to do my button all's we have to do is call this batcher of any UI element we have so anywhere on the application so you wanted your whole application be able to jump onto the the UI thread to do this if you're already in a UI element here you can do the spatula invoke which is fine but say you're not even in an object that's there's inheriting from the the UI hey you can instead do application drop current and then you can get dispatcher from there and this dispatcher is the same as that one which is the same as my button dispatcher so that's why you'll see in some WPF code you'll see something like application current got spotted or invoke and then people doing that to effectively jump back onto the UI thread if you will and that's perfectly valid that's how you can do it there's no problems with that at all so you just have to be aware of in applications for UI that this is when threading becomes a bit more relevant and a bit more obvious that if you were to do things on the same thread as the UI thread you have to be aware of what thread running on then you can unlock the UI thread and at the same time once you are you've done with your work and you want to edit anything with the UI you should also doubly make sure you are back on the UI thread so there's two kind of phases to the work which you'll see on the you know the WPF for set a word application were making is that this is why you see a lot of Tarun's is to jump on to the actual or rather make sure we're not running on the UI phone because we don't know who's calling us or where from so it's kind of an assurance that we're on the work off the UI thread and then whenever we want to get back to UI we'd call this application current dispatcher invoked to make sure back on the UI thread the reason you won't see much of this happening in my application though is because we use view model binding and by default mvvm will jump us back on to the UI thread because of basically just how it works I'll get into too much detail it listens out for notify property changed and those callbacks happen on the UI thread so this isn't required but anyway that's kind of another you know another topic again so the last thing I really want to touch on here I don't want to go too far into tasks because like I said that's not the purpose of this video it's purely the whole understanding threads and getting that concept and you had first before we move on to anything more complicated and I but I do want to show that again this is not typically how the code is written for a few reasons one is because this call is synchronous so it's good Savoy it's just a normal function so it's gonna run line by line so it's gonna run this line then this then this and then because that's in itself contained and runs it goes to this line and this line you can see that happening if you add a breakpoint here in here and step through you'll see what I'm talking about so if I click the bottom runs this line then effectively this line here where the yellow is this top line and the next line is down here because the parts inside here this is now we're gonna spin up afterwards on a new thread so this is a synchronous call that happens line by line so it goes here to here to here and then asynchronously it's gonna jump back and come into here and then this is now running independently and because this is a task and how we want this to work like say typically wouldn't say that the contents logged in inside this function you'd simply want to await this function you want it to look cleaner than that so instead of what you'd normally do is do a wait and in order to await something we need the calling function to have a sink before the type and again I don't want to go into too much detail but you'd rarely ever and almost should never have async Boyd because you'd expect it to return something so to be an async task but in this case it doesn't matter where as a special situation where it's a event to you know a click and nothing's actually awaiting this we just want to be able to wait inside so let's first look at what this does to the threads and then I'll explain more so now we have an asynchronous click event so now we click you can see we're still on the main thread here so nothing's changed we're stalling the UI thread we step over and in fact I should have clicked down there first to make sure we see the exact same behavior so click here run thread one and you can see now it's gone into here and it's on a new thread and that's because we were weighted so now instead of going to here you know continuing it hasn't it it's now stopped so now we haven't got to here because it's now awaiting this task but importantly the UI thread won't be locked and we'll show you that afterwards in a minute but now we won't hit this line you know finish our function until this task is finished running so now it's gonna do the usual and we're still on the same for that and then when we finished it's going to get to here it's going to download but you can see here now we'll jump back onto the UI thread and don't need to do and now we continue here so there's a slight important difference here is that before we'd go into this function on thread one we'd call this line here on 31 and then we'd instantly be done because we weren't awaiting it so that's it we're out of it the difference now is that it runs almost like in sync you can think of it synchronously and that's exactly what happens so the code line goes here to here to here to here to here and then when that's done it comes back out so you can think of it now almost like when you're visibly reading the code which is the whole point of tasks and awaiting it there now reads in this flow it naturally happens so we were to then down here say my button dot or rather let's just output some debug so we'll say debug dot write line one and we'll do just took a few inch they can can see what's happening and you can see the order at which it all happens and you can see that all these should go in in sequence now so you have one two three the other thing that won't go in sequence actually is this because this is jumping off to another thread and we're not awaiting that but the invoke should be synchronous which it is so you'll still see that in order five six so now if we run this and click the button everything should still happen but in terms of what happens here it will look like it's happening in a synchronous fashion so you've got one two three four five six but the important difference is if we were to now output the thread coven come on thread dart managed ID as well as the number one you can visually see then the the difference is that even though we're running what looks like synchronous one after the other were bouncing back and forward between threads but we're not having to think about that so the task is handling that for us so it kind of alleviates that work so you can see one is on the UI thread then two and three aren't on the UI thread and then for when I've asked it to get back on it is back on and then fired off again and then importantly once it's finished awaiting we're back on the UI thread again so you can visually see here sort of there the progression of it bouncing between threads and like I say the important thing is we don't have to worry about that we are we are having that all done for us without something to worry about that so let's clean this code one more step and get to the point where this is how I would use a task so say this was now a real bit of work and this is what I want to actually happen we've now done async so we can await the task so it runs in order and the point of that is so that after we've done and we're finished here we can do something with the UI so now instead of having to do application code dispatch it I invoke and all this stuff we can now just cut all that out I wait the work you want to do and then finally update the button and the reason this works and as you've seen just is because we come in on the UI thread we tell it to await this task which now spins off its own thread to do the work so it's not blocking the UI and now we're on safe threat number four in here or you've seen that after once it's finished and come back off the task into here we're back onto the UI thread so now if we run this you should see that we're on thread for their heavy continues to use the same numbering so you're on thread 4 and when it's finished downloading we're now on thread 1 here and then importantly it's updated the the text so we do that once more and you'll kind of see that so it says how though we click it and it's still responsive and then when it's downloaded it then says logged in so this is exactly how I would actually use a task await and task top run and like say I don't want to delve too far into tasks because I want to do a video purely on tasks oh and the one other thing I would do is you wouldn't do dot result that's a rare occasion would you ever do dot results on a task you would await the task just like we've awaited this one so to do that you don't really make there the thing you're passing into the tasks are on async as well and now you can await the result correctly so you you should never really get a task where you return a task and do dot result this is very very very few cases where you'd ever want to do that because that can deadlock for again reasons I don't want to go into but this now looks like a real function and this is exactly like I say how we do do and would do things in UI to make them work correctly and hopefully you can visualize and you can think of it that when you do a task top run once you're in here you're on different thread if you will and when you come out of it you're back onto the calling threads or whatever thread you're on here before you call the task will be the thread that you're on here after the tasks that's unchanged it's the bit in between that jumps itself off to a different thread so the thread here is the same as the thread here and the await simply makes a nice clean way of doing something here on a different thread but simply coming back and continuing on the same thread now there is one exception to that and at the end of a task or on or at the end of any task you can say configure a wait and that's configuring this await and the only option is continue on captured contact so in sort of quick and simple terms if we were to pass in false what this means is as you mentioned we start here on a certain thread which is the UI thread when this task wants to await and start effectively do this work and then come back onto the same thread the configure awaits false says don't bother so what it means is that once you enter this task don't bother try remember where we were don't give a crap about this thread that we came in on just simply do this amount of work and then when you finished spit me back out here on whatever thread suits so that may or may not be the original callin thread and there's again there's rare occasions where you want to do that and some people try to use this to avoid UI deadlocks and mistakenly which is if inside basically if you do something inside this task which is awaiting the original thread that you're on inside here you end up with a deadlock because this thread is waiting for this thread but if this thread then waits for that thread yeah yeah no with a deadlock and by doing a continual wait sometimes you're trying to avoid it but that's not the purpose of that that's this is not meant to be used that way but in order to show that happening and we just simply put that configure await in there say don't bother we turn him back on the original thread it came in on when we click you can see what I'm thread for inside and then you can see now we're on thread 11 this whole new thread we're on a completely separate thread because we said we don't care effectively about returning on the original thread and then obviously we now get the exception that we're not on the right thread for the UI which is correct so the issue with this or not necessarily issue we have to be super careful as if you ever see this or ever use it even though visibly looking at this you'd say we've come in on a UI thread we should come out on a UI thread that's not necessarily the case so even though this code looks like it's written that wherever you are here means you're in the same thread here that's not the case because this changes the context in which all this code is running so it's a pretty complicated thing to explain without a lot more detail about tasks and understanding so I don't want to take that any further than that but hopefully we've covered enough in this video that you get the point of threads in general you know what they are what it means and it's simply another task that then runs in parallel with you know whatever other tasks that your your application is running I've shown you how to use them create them explain the foreground and background threads and then just briefly touched on tasks which ultimately rely on threads so hopefully that's been a useful video but we'll continue this up next with another video where I talk about specifically tasks and go into the detail of the awaiting the async them running that continue within all now very specific on tasks but until we've done this video I can't really start with tasks because you need to understand the underlying mechanism at least you know in a short and simple way so hopefully that's been enough information that's useful and again as always if you want to support what I'm doing I have a patreon page that pull the link in the description any you know support for what I'm doing is greatly appreciated any questions on anything in this video just simply ask how do we get back to all comments and questions and as I say hopefully this video is useful I'll see you in the next one [Music]
Info
Channel: AngelSix
Views: 67,509
Rating: 4.8694563 out of 5
Keywords: c#, threads, tasks, multi-threading, deadlock, deadlocking, cross-threading, parallel, tutorial, guide
Id: XXg9g56FS0k
Channel Id: undefined
Length: 67min 4sec (4024 seconds)
Published: Thu Mar 29 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.