C# Advanced Async - Getting progress reports, cancelling tasks, and more

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
a sink in a wait can be really simple to use in c-sharp but sometimes you want to do more than just the basics in this video we're gonna curse on those advanced features of a sink in a way including cancelling tasks and getting status updates on tasks as they're running if you aren't clear on what the basics of a sink in a way are watch my previous video on a sink and that covers the basics they'll be a link down in the description to that video now in case you're new to this channel my name is Tim quarry and my goal is to make learning complex topics and c-sharp easier on this channel you'll find videos on a number of different topics including a complete course I gave way for free just to help you understand how to build a real-world application from scratch if you ever get stuck or huazi a topic covered just leave me a comment below and while you're here make sure to give this video a thumbs up so let's jump right into the demo here we have an app that's similar to the one we created our first async video let's walk through the parts of it and we can talk with a new stuff after that so first of all we see we have a form here with four buttons now it's a little bit more than how you four but we're going enable some of these new buttons so for if you have a normal execute and what that does is it goes out and it executes the task and it's just synchronous there's no asynchronous codes it's the you know the normal way of doing it I guess he called and that's gonna lock up the UI for you know 1 to 4 seconds or so while it's running and then we have the the async execute and this is essentially the same thing as the normal except we're doing asynchronously and so the UI is more responsive we can move it around a lot kind of stuff while it's running then we did the parallel async execute which we did last time our last video and what this does is it it executes all the different tasks we have in fact we have like 10 different tasks we're doing it execute them all asynchronously but doesn't wait for any of them until the very end it waits all of them to complete it so essentially the tasks are operating in parallel not in sequence and so it's asynchronous it allows the the UI to be responsive but it's also much faster because we're not waiting in line for the next one to execute than the next one the next one and finally have this button here called cancel operation and this is a new one we're gonna add today in this video and that's the idea that while we're executing an asynchronous task or even a parallel a Singers task but mostly just the async res task while we're executing it we want to be able to pass in this cancel operation and say you know what we don't want to do anymore let's let's stop and so allow us to stop a long rain project or process that's running asynchronously we also have this right here which is new it's a progress bar so one of the things where I learned today is how to get information back from an asynchronous task that doing a long-running process asynchronously and so the idea is you can you can bubble up a message it says hey here's where I am you know as far as I've done three tasks on a ten-ton for task 10 something like that and so what I do is we're gonna track that progress on progress bar it'll actually be a real progress bar meaning well we have one task ahead of 10 done the progress we get 10% we have five tasks at 10 done it'll be at 50% and so I actually show us a real look into how that's that code is running in the background and inside down here we have our results window which is just a text box that shows us what we've done so that's all it really is for the the user interface portion of it let's look at the code behind now again I'll give my same as claimers last time I'm doing stuff in the code behind on add a PF form I'm not using mvvm these are all things I wouldn't do in a real application but this is a demo application so just make sure you're following the things that I say I do you know follow along they are best practices but the idea of the way I said this demo up is not best practices I would not set up a DPF app probably at all without our mvvm but definitely for a larger application I would use mvvm but also have a class library separate out I would have as little Co as possible and my code behind all these different things are important to do in the real world but for a demo application it's okay to kind of shortcut some of those things so that's my disclaimer let's move back into the code so let's say the code behind for my main window so this main window we have a couple things we have the execute sync click a sync click parallel a sync click cancel click and then the print results so these are the the events behind our buttons and they're pretty similar in nature we start off by Korean stopwatch at the end we stop a stopwatch get the time it took and then also we print out to that results window the total execution time that way we know how long I ran but inside of the stopwatch start and stop we have this download async which we'll look at in a minute it gets results which is a list of website data model and we have send that off to the printer so the print results which is right down here just loops through each one and says here's the results of the website that was downloaded and the size of the data that was downloaded so that's all there is for you know the the sync does it cause the download sync run down let's sync the async which again has an async on the method call it awaits for the run download async but everything else is the same the parallel async does something pretty similar it it passes in or it calls the run download parallel async and it weights that and prints it out so it's it looks functionally identical to the async the difference is it's calling a parallel async method and so we'll look at that in a bit and there's nothing for the cancel so let's look at these methods they're all in a demo methods class and this is all static so we can just call it directly don't have to instantiate the first and first thing we should see right off the bat is this prep data which essentially just puts a list of websites into a list of string and so that's all the websites will be downloading everyone uses the same list that way it's the same it's not different per synchronous versus asynchronous or anything like that so you can compare apples to apples so there's our prep data so you'll see this call at the beginning of every method so run downloads sync that gets the data and then it creates an output list and that says okay for each website in our list of string I want to download the website get those results into a website data model class add that to my list and the very end send that list back so it's just little tweaks and we did in the previous video clean some things up change some things it's just a bit but the most part it's exactly the same asynchronous it does the same thing as synchronous it's just that we await again async in a wait we await the download web sites async call and they also have the parallel which does only is very similar to the run download acing it's just that instead of a waiting each call we add each call to a list of tasks website data model and so what happens is we have is list we add a tasks to it as they're running at the very end after he called all the all ten websites lowered them in today do this asynchronously and kept going in wait for it at the end we said wait for all of them to be done so when all then grab those results and then we send those results back as a list of website to em all they come out as an array so I have converted that array to a list instead so that's our three methods down here we have a couple of private methods the first one we'll look at is actual skip this one form it look at this download website this is the synchronous version and so it essentially just opens up a web client it creates a new website data model this is the output so we know what the website was that was downloaded and how much data was in it so we call this on the web client we say download string which is a synchronous method you pass in the URL this is all baked into the dotnet framework as web client we download the website we put that into the website data of our output model and we send it back so this is all synchronous the exact same thing happens in the asynchronous download website async but instead of calling the download string we call the download string task async which does it which is a async version of the download string and so it we can wait for that and then send back the results we're done so that's that's all we have so far and in fact let's run this ray as it is just so you can see now normal execute my windows locked up I can't move it around when it's done then we have our list of the ten websites and a total execution time don't forget that the first tyrunt this it's slow so the second time you run this it will change to you know under two seconds a sink execute allows you to move things around while we're waiting and now it's done against a mad time parallel notice the parallel is done in half a second versus the two seconds of a sink so parallel is very quick and it does allow us to move the window around just to show off that it really does it's hard when so fast for the first time you run this remember it's slow so I'll execute a parallel and then move the window around so I'm moving the window around and it's done let's do it one more time just to make sure that you caught that so that's just a starter boilerplate stuff we have so far this is we're going to start today to move ahead and make things a little better a little more advanced so the first thing I want to do is I want to tackle the idea of getting information back when we execute the async so right now if I were to start this and do the async execute I get nothing until it's all done when it's all done then I have the information we can do better than that so let's let's do that so let's start by going into our demo methods and looking at this run download async method this is one we're calling from our event so in this method we can pass in what's called an I progress it's a generic so you have to give it a type let's get a string for now we're going to change this blood string let's call it progress so what this allows us to do is every time we make progress we can call this and it will bubble up an event to the caller so we do this as we'd say progress dot and there's only one real method here it's report so you'd say okay and then we pour our value back in this case a string that's all there is to having progress reports in our asynchronous calls so this progress right here will say okay here is the report the report is testing which is useless right now but it gives us information back so let's create a model that will have more information about where we're at in the process so let's right click on our project and say add class and we'll call this class the progress report model make it public and let's add a couple of properties in here the first property when I add is we have this list of website data model the this is the websites that are currently processed and notice we add a new one every time we complete the download web website so let's let's add a this model right here let's call this the site's download and we'll initialize that start off and that's also add another property that's an integer that is percentage complete start off at zero now percentage complete it's an integer can't be you know 0.5 point six point seven we're talking a number between 0 and 100 because we have this progress bar here that takes a number between 0 and 100 that'll be our progress percentage so we'll have we'll pass back here's how far work we've gone here's which percentage we are complete and then also here are the sites that we've downloaded so far so with that instead of passing in a I progress of type string will do product port model and then let's create a project port model let's call report equals new progress report model and we're going to say instead of testing here will pass in report so right now we have said here's our I progress which is built into the two system as a that's a system interface therefore we can don't worry about adding that so I progress here's our model are passing in or the type it is gonna be they're passing back actually and here is our variable all to do is call that variable dot report and pass in our product support model instance that's gonna pass that instance back to the caller so let's populate this instance with some values so first whereas a report dots sites download equals output so that's gonna grab our current output list add it to the sites downloaded and so now we'll know ok downloaded three sites and then next what we'll do is we'll say a report dot purse ang complete equals we've got come up with some kind of formula for the percentage complete well we know that we have to download all these web sites so that's our total in this case I know it's ten but let's pretend we don't so was to web sites doc count that give us our total we know how many we've downloaded so far because that's output every time we download one we added output therefore we can say is output like a spell dot count that's multiplied up by a hundred divided by websites dot count so am i doing here well I take the total number of the output which let's just pretend we downloaded two sites that be two we know that websites count is ten well 2/10 will be a percentage it won't be a whole number that's a problem so instead we're going to do is we'll say instead of being point two we'll say two times 100 that's 200 divided by 10 is gonna give us 20 which is 20 percent and so by doing this way and this order I can use integers but these are all integers and not having weird rounding issues where I I lose information so if I always say output dot count divided by website count times 100 that wouldn't work because I'd have either a 0 or a 1 because they're both integers an integer division gets rounded to the nearest whole number so doing this way I get a number between 0 and 100 so now I have my percentage complete and I can pass back that report now in my call here it's yummy because I haven't passed in an i progress instance so let's create one of those let's actually do it outside of the watch so before the watch will create the instance we'll call it its progress and it's of type progress report model so again this is built into the the system namespace and it's part of the.net framework so we're good to use it so progress of type progress report model now let's call this there's progress equals new private port model and then we're gonna say we look up an event so progress dot progress changed it's the event we can hook into plus equals let's call it report progress oops and it's gonna say hey you haven't created that event yet let's go ahead and generate that method and that's right down here so all just doing is creating a vent for us just like we have events here and it's got an object sender and a progress report model II so it's passing in that progress report model as our e so now I can use that e to do it every want to get the values so I can say let's figure out what the the name of this this is right here it's called dashboard progress so I can say dashboard progress dot value equals e dot percentage complete and while I'm here I have this method called print results that takes a list of website data model so let's go ahead and say print results e dot sites download so it's gonna download or it's going to print out what's been downloaded so far so with all that done now I just pass in in this execute async I pass in my progress so I've got this instance of progress where I've wired up an event on the progress changed event to call this report progress event color or event method and in that event I just spit out okay here's a percentage put that into the progress bar and go ahead and call this print results which prints out all of the websites have been download and the length of each of them so let's just try this and see what happens we can come back and look at a little more in depth so let's run the async execute and there you go we got a progress bar and it it goes out one at a time as we're downloading the sites that's it go faster this time because of the second time you run but now you can see as we're running what's happening and it's still responsive so even as is downloading it's still responsive and yet it's updating a progress bar and it's updating this results window so that is how we do progress it starts off by passing in I progress object of whatever type you want and so I recommend you create a type that has all the data you want in it and then inside of our asynchronous method whenever you want to update the status which in our case it makes sense to do it in the for each that we have ten status updates one per site we just call the progress dot report it's the only method there and it's just saying okay send back the whatever type that you requested so this case we have a progress report model which again that that's the custom one and so we're passing that object back and before we do so we populate it with the relevant information that information goes back to our caller back to the right here so in this this object we've added an event it says listen for any time progress changes call this method and so therefore that's what happens every time you call the report it fires off its report progress event which takes the data it's being passed in and does something with it in this case it reports to the screen via the progress bar and also the results window so that's all there is to it that is getting progress asynchronously from your method so our methods running asynchronously we're getting progress back as we go the next thing you want to look at is the idea that maybe we don't want to continue on if something's running too long say for example I start download these websites so I've got 10 websites that I'm downloading and I start the process and then realize I have no internet connection it makes no sense me to wait for timeouts on 10 calls to the Internet when I have no internet and so I want to have some way of saying ok stop that my bad let's try again later and the way I do that is to have a cancellation token do you pass in and so let's set one of those up so let's do this right on this execute async method again since this is the one where we are actually doing things asynchronously but we're doing them in order we're not doing them parallel makes a little more difficult between parallel to cancel asynchronous method because you kinda have to wait until after something is done before you cancel so for example grant put the cancellation right here or we say hey go ahead and cancel it they requested it and that's that I'll check in 10 times after we download the first website the second website a third website it'll check in each time so it makes more sense to put in this one so let's go ahead where I started coming at the very top of our main window class and outside of any of our methods we're going to create a cancellation token source now this is in a system dot threading namespace so it may be I've got this ad already but if you don't have it ad then you'll throw an error so let's let's just show you how let's call it CTS and we'll say I just say equals new cancellation took a source right there now again if you don't have threading you're gonna get this and you have to just do a control dot and say using system dot threading or as typing yourself it's up to you so now that I have this cancellation token source what this is is it's going to be the thing that kind of controls whether or not we cancel or I call this and say go ahead and cancel and that's going to cancel any method that is currently running that uses this token so let's come down here to our execute async and we're going to pass this in to our run download async so I'll pass in as C T s dot you pass in just the token that's what I pass in now again it's not it's not ready for it yet that's okay we'll go ahead and make it ready for it in a minute so let's come over to our demo method all right say okay we want to pass in let's hide this I'm going to pass in a cancellation token again that's from the system depth reading namespace so if you don't have your using statement for system dot threading you'd have to go ahead and add that and let's just say cancellation token we could call cancellation tokens as C T is probably better let's do that so you pass in that token and then after whatever we need to check this periodically and so if the user says and go ahead and cancel it won't actually do anything until we get to the point where we check to see if it's canceled so I just make sure you understand that it doesn't happen immediately it happens after it's hits so I think the logical place to add this is probably after we do our first download I'm assuming that you won't be so quick to hit the cancel button that we don't even run the first download therefore it's no point in checking up above this so we're gonna do cancellation token dots and the method down here is throw if cancellation requested and it's a method returns void that's all you do so it's going to happen is if the cancellation token is activate if we say go ahead and cancel that task we're going to throw an exception essentially and then exception is going to be the operation canceled exception so it's gonna say hey you cancel the operation so it's done now that allows us a couple of benefits first of all it's gonna stop right away and go back to our caller but it's also going to allow us to do any kind of clean-up on the caller end that we need to do before before we continue so say we hit open connections or something like that we can go ahead and close those now the other thing I note is we should do some cleanup in here if we need to before we actually throw this if you notice but there is the option here is cancellation requested that can check to see if the cancellation has been requested so we do an if can't slice is requested go ahead and do cleanup and then throw the if cancellation request it so we're going to do that because it's a little overkill what we need to do here but I want to point that out so now we have this cancellation token in place all we need to do is have some way of calling that while this is running and that's pretty simple we have the cancel operation button which has a click event and we'll say CTS dot and we have a few options here but one of them is canceled so that will cancel the operation now remember I said it returns a an exception or it throws an exception so you need to make sure that we wrap our call let's actually wrap both of these in a try-catch so here's the results and printing results we're not going to print the results if there are no results therefore we'll wrap both of these in a try-catch so another snippet menu I have surround with I can surround this with a try and we'll capture just the exception we need which is the operation cancelled exception in fact we don't need it that e^x either because we I won't use it so let's just just grab the actual exception we're not going to read through it instead we'll just say let's grab the results window and we'll say plus equals and I'll do my string interpolation and just say the async download was canceled and let's add a system or I'm sorry an environment that new line at the end of that that way we since we are going to write out the time elapsed afterwards I want a new line there so that you it doesn't continue on the same line since I will do we're going to a try we're on catch we don't have to do anything the exception except know that if it's the operation cancel exception we print that out to the window and then we continue on we stop our timer and we go on our way let's run this now remember the first time you run this it's slow which is actually good thing that gives me a little bit of time to cancel the operation so let's execute async it's starting I cancel it so now I've downloaded one two three four five sites but then I cancel the operation and the total time was just under two seconds and we kept halfway done so that's all there is to it we create the cancellation token in our caller in this case the form I did it outside of any method so I can call this CTS down here my event to say CTS cancel but we pass in the can the CTF a cancellation token source dot token passed that into our asynchronous method and that's it on this and except for wrapping a try-catch on our async call side what we do is that we watch for that exception or that cancellation took a need triggered and what is we throw the exception that's why they catch that exception and just do something whatever it is like in this case just update the results window and then we're done so now we actually have an asynchronous method that reports back to progress and that we can cancel halfway through so that's really it for some advanced async stuff I mean we've cancellation tokens and reporting progress that's that's some pretty advanced stuff for async and I think should cover most of your most of your normal needs now it's gonna be some stuff where you might need get into the task parallel library and do some advanced stuff there bye the for the most part just calling a sink in a wait and maybe getting the progress of it or being able to cancel it or all you really need now I do want to touch on one thing that came up a couple of times and that is this execute parallel async so this right here the way we did it and let's go back and look at the code for this the way we did it was we create a new task that was an asynchronous call to download the website we created one for each website but we didn't await them instead we just add to a list and then said when all grab the results and return that list and so the question was raised well yes but what about the parallel for each that's part of the task parallel library and it allows us to do you know it runs in parallel so why would we recreate the wheel and do it this way so let's go ahead and create a demo for this I'm actually going to come up here to the Run download async and copy this whole thing I'm sorry Road that let's sync not a sec and I say run download parallel sync what we're going to do is instead of you have for each I'm gonna do a parallel for each just kind of show you the difference so let's just do parallel dot for each and the type that's returned or passed in is type string and so the first thing you pass in is the websites and then what you do afterwards is you say let's give a name called site and you say run this code this code right here and then at the end of that like so so I kind of really brain die for each they walk you through why did we do parallel not for you you have to say this is what I'm passing in this case I'm passing in a string so this list right here of string it does okay each one the string so that says okay pass in your list and then for each item we're gonna do an action now an action is a a method call essentially in line that doesn't return a value so it's not returning a value is just making a call in this case or saying is do these two things now I'm passing in this variable site which I created but what site represents is each one of the websites so it essentially it's a shortened version of for each it's a little more complicated in some ways but once you kind of understand the syntax it makes a little more sense so I've created a variable on the fly called site I'm passing that into each for each of these items in website it's gonna call this bit of code and so it's gonna download the web site and that's the site it's gonna download and it's gonna add at the output so this is the same essentially as run download sync except for the fact that enough for each it does each of these downloads in parallel to each other so that's what parallel nut for each does this is okay I've got a list of things do each one in parallel the others so let's just change over our run download sync to run download parallel sink over here in our method call in fact let's run it first just to show you if I run into normal execute it takes about four seconds or so to download yep 4.2 now that again and it's 1.8 so almost 2 seconds let's go ahead and do nothing else except change this to use our now parallel sink so if they use that the parallel dot for each let's run this that's acute normally and the first download takes 1.8 seconds but when after that takes 0.4 seconds and 0.7 seconds and point four seconds so it executes in a similar manner to our parallel async for each as far as speed goes notice they they both operate about the same speed and so the difference is that let me show you again let's starts over again I'm gonna run this again once I click start I'm gonna try and move my window and watch what happens I can't move it now I can move it and so that's the difference between parallel for each and doing it on our parallel async where we actually create our own parallel execution the difference is that this parallel dot for each that's right there the parallel dot for each this does do them in parallel me doesn't all at the same time but it doesn't all the same time synchronously meaning it locks everything up until they're all done and so that download the longest download that's how long we have to wait for this to be done now granted it's a short amount of time then the miss up here we're waiting for each one one at a time and the the time is cumulative so there is a benefit there but it still is synchronous okay so that kind of touches on that but let's look at some ways we can kind of take more advantage of this and the reasons why you might use it so parallel dot for each there is some optimization that goes on underneath the hood that kind of makes sense that we want to use this especially if we have a long list like from a database where we need to process each record or something like that parallel for each makes a lot of sense because what happens is it spawns off tasks and batches them so it's been a lot of intelligent work behind scenes to say here's how many tasks I should be running and here it's actually doing them now again it's not creating the asynchronous tasks that allow us to get control become a user interface but it is spawning off those batches in a way that makes sense for the processing so we can do is actually wrap this whole thing in a task dot run so let's in fact let's just make this asynchronous and let's just change the name fact you know what let's make a copy of it that way we have both versions it was a parallel async which I believe you already have one yep called parallel async it's called v2 so this is version two the parallel async and so we're going to do is wrap this whole thing let's grab all of this I am going to just ctrl X to cut it out and say let's a weight task run and this is our same syntax whoops there we go all right and we have to change this to be the public static async alright so now we've done is and so one more thing we have to change this type to be type task list website data model okay member that whenever you have a sink it needs returned tasks or tasks of T in this case tasks of T which T is list of website data model so everything else about this once we make those few changes stays the same so we can still run this asynchronously we're still awaiting things it's gonna do it in parallel all that fun stuff so now let's go back over here to our event where we say execute parallel async you know let's call a version two method which is that parallel dot for each and let's just see how it it behaves parallel execute we can move our thing around and it's done 1.8 seconds second time is 0.3 seconds so again we now have control of our UI and yet we're doing things in parallel using that parallel call so now we can do is we can actually up the game again and we can grab the the progress report option let's pass that in and then in our for each we can actually update our progress report let's grab the the code for that and we'll just paste in down here and we have to actually have our variable let's put that in right there so now this is the same as the download sync where we have the project report and every time we add an output we're going to update that progress report so now we'll do the same thing that we did in here or we'll create our progress report we'll wire up to the report progress event so let's grab those two things and paste them in and then we'll pass in our progress and that should be it so let's go ahead and execute this it's now parallel async execute know it says doing it in parallel it's doing it reporting back was doing and it gives us our execution time all the same time so it's kind of the best of both worlds there in fact this really is an upgrade to what we had in our previous parallel so version 2 actually is better than version 1 let's go ahead and collapse these down so the reason this is better is because we actually have the ability to report our progress one at a time whenever they're done so again if I do the parallel async it's down low and notice the order is downloading Microsoft is firsts with a thousand twenty characters twitter is last at a hundred and seventeen thousand characters if we did the async execute Yahoo is first and Twitter is not at the end so notice the difference the reason why is because that Microsoft College is third here that's the quickest one and so it's the first one that completes so it rearranged the order and yet we're getting that that statute port every time it's being done so it tells us the exact one it's being done and when it was done if we want to capture that information too but also the percentage as we go running in parallel we can't really get that here because of the fact that we don't minority's tasks for their completion instead we wait for all them to be done so we don't have a real good mechanism to say okay now that you have done something go ahead and report back now we could dive down to this and pass the the progress down to this and say okay to put your progress from this the problem is we might not have access to this now if we did we could pass that down it could probably report some progress back but notice that down here we don't know about any of the other websites so we don't know which ones are done which ones aren't so that's gonna make it a little more difficult to track who is done what so just make a little more complicated when we're doing it in parallel using this method we originally came up with now this method we came up with it is good for a lot of things but reporting progress isn't one of them so this parallel dot for each when you wrap it in a task dot run allows us to spawn off all those different threads to execute these actions and yet it allows you to call back to our calling thread without doing that marshalling and all arrests or invoking and stay what does is says okay I'm gonna pour back to you this is what I've done it allows the report on every step along the way which means that this method allows us to have both the progress but also the parallel so just you know a couple different things we can look at if you're doing something simple this this right here call add a task to a list when all that's really simple but if you want to have some some of the more advanced features like the progress then you're going to have to go ahead and upgrade to something like this where you do a parallel that for each rat than a task dot run and I weight that so it's kind of up to you how you want to go how complicated you want to be but the features are available to you either way all right so that's it so we've covered the idea of having progress in our asynchronous tasks we've covered the idea of canceling our asynchronous tasks as they're running and we've also looked at how to run things better in parallel to also give us a progress report as we go so and all of that runs a nice UI that's really responsive and it gives us some fun stuff like the progress bar and the updated results window so now we're kind of creating apps where they really are real world friendly you know people don't like to just sit and wait hoping the applications running now we can show them yes it's running and here's what it's doing and so that's that's really nice the one thing I would recommend that you think through is if you do have this idea of a button that executes asynchronously that you grayed out and say running in here so you know disable the button you know set the enable default or something like that and then say you know waiting or running or something like that in the buttons text that way people know not to click it over and over and over again otherwise you may have five six seven different calls that you really don't want to have when one call off especially if you're you know posting data or uploading a file or something like that you make sure you only have one of those so just make sure that you use lock down those buttons so they can't do something else since the UI is now responsive all right so I think that's it for this video I hope that you got some value out of it go ahead and let me know down the comments you know what your thoughts are it is something I missed it's something you want me to see any cover in addition to this or even I just on different topics all right have a great time coding
Info
Channel: IAmTimCorey
Views: 178,560
Rating: 4.9741659 out of 5
Keywords: .net, C#, Visual Studio, code, programming, tutorial, course, training, how to, tim corey, C# training, C# tutorial, async, await, task, threading, parallel, parallel.foreach, async await, advanced async, wpf, best practices, task.run, context switching, asynchronous, c# async, c# (programming language), asynchronous programming, c# tasks, c# task class, c# multithreading, c# best practices, async tutorial, c# async tutorial
Id: ZTKGRJy5P2M
Channel Id: undefined
Length: 52min 21sec (3141 seconds)
Published: Fri Feb 02 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.