 Hey everyone welcome back this is Brian and in this video we're going to recap all the different ways we can do threading in Qt and we're going to compare them we're going to go over the pros and cons of each one. This video may seem redundant but I want to make sure we have all of these concepts solidified because we are going to give a graphic user interface example using you guessed it Qt widgets. This is what Qt is really famous for is user interfaces because let's face it people eight command lines you know actual people users want a graphical interface that they can do something with. So we're going to show a very simple example and tie all of the threading conversation into why it is so important especially when you get into a user interface. Spoiler alert I just want to cover a few things super quick so bear with me. These videos on YouTube are different than the videos I have out on Udemy. Udemy are full blown courses and basically this specific video is kind of a combination of Q6 core for beginners intermediate definitely advanced and we are sprinkling in a little bit of widgets for beginners. Now you'll notice that it's Qt five widgets and this is a Qt six video I have not yet re-recorded widgets or QML for Qt six but fear not there's minimal changes I actually went through thousands of lines of code and for the most part widgets and QML are very backwards compatible with Qt five so you can still sign up for those courses and have minimal problems. Also well I'm going to record those in the future because it's fall time here around Halloween and that means the leaves are beautiful but also falling and every single one of my neighbors loves their leaf blower. I mean really loves their leaf blower it's all I hear for about two weeks straight so I don't do any professional grade recording in the fall because I'm sorry you're going to hear leaf blowers in the background try to remove them but I can still hear them so bear with me. Anyways let's dive and take a look at different threading models and how we can get through this. Okay because this is a recap video we're going to start at the very beginning I'm going to say new console application we're going to do this with our testing this is going to be Qt 6 and this is episode 26 and hit next I'm going to make sure our build system is CMake because this is a Qt 6 project again doesn't matter you really could do Qmake but we're going to be using CMake just in the future and just next next make sure you have whatever kit you want as long as it's Qt 6 or higher and ta-da there is our application in all of its glory just gonna paste in some notes here again we're testing different ways of threading pros and cons and we're going to end this with a graphic user interface example so first thing we need to do is we need to go in here and we are going to make a class that we're going to work with and we're gonna call this well appropriately worker you can call it whatever you want but I'm gonna call it worker important thing make sure it's a Q object and it has the Q object macro because we're going to be working with signals and slots next next finish and let's pop in here and we have to do a little bit of surgery on CMake list so first thing we need to do is actually add the files that we just generated some say worker and we want the implementation file along with the header file we're not done yet notice how we have core we're also going to work with concurrent in this video remember Qt concurrent which I started at the very beginning when we talked about threading is probably the most simplest yet advanced high level way of doing threading we're going to cover that in this video so we have to add it so we're gonna just simply say concurrent so we're saying core and concurrent are both required and we're gonna tell CMake to go out and find those gonna do the same thing down here and we are going to beg borrow and steal using copy and paste and voila save and as long as you get this nice beautiful green bar down there you're good to go all of these little issues should clear right up and if you're super paranoid like me you can just open up the file and hit build just to make sure that CMake is functioning properly now that we've gotten to this point well we can start working on the actual code so I'm gonna go in here gonna paste includes again in case you're wondering how that magically happened I have notes off the side of the screen here so I could just copy and paste as needed to save a little bit of time alright let's go in here we're gonna say worker my horrible spelling included it no extra charge there we go we're gonna make the deconstructor now we have signals so we're gonna add in signal called finish now remember we don't implement a signal this is specifically for cute cute is going to implement that using mock later so if you right click this and go to refractor you'll see there's no way to implement it now what we want to do is we want to implement a runnable and let's go through our includes are a quick we've got Q object Q thread Q runnable Q debug Q event loop something which is new this is a way of actually stopping a thread but still handling event loops we're gonna demonstrate that here in a second meaning we can pause that thread or I should say lock it up but at the same time still get signals and slots it's actually very cool we're gonna use Q scope pointer because we want memory management and a Q timer so we're going to implement our runnable I'm gonna do that by just going here say we'll implement the public part of runnable right click runnable go to refractor and insert virtual functions of base class if I went too fast public Q runnable right click this reactor insert virtual functions of base class then you want to make sure that we're going to implement run and hit okay and it's gonna add this little guy here you can actually get rid of that if you don't want it that we don't have that ugly comment there now we're almost done with the header file here we're gonna say public slots and we're gonna say void work now we need to actually implement things so I'm gonna right-click work refractor and add definition in our implementation file you see run is already in there for us and it's all of its glory and let's go ahead and implement our deconstructor so far this is actually fairly simple this class really isn't going to do a whole lot but what we are going to do is print out some things on the screen again the joys of working on command line it's very screen-intensive and here I want to do something a little different we're gonna say Q underscore funk info this little guy right here is awesome it will print out on the screen all of the function info basically it'll tell us the file the line number and all that fun stuff so we know exactly where we are at our code and then we're just gonna say Q thread current thread just print out whatever thread we're currently running on the thing I love about using this Q Funk info is we can now just copy and paste that everywhere and tells us exactly where we are in our code all right quick recap we have our constructor we have our deconstructor we just simply want to see when this is constructed when it's deconstructed we have run run is part of the run a bowl back here and this is used specifically for thread pools but we can call it directly so we're going to do that this is where the bulk of our work is going to happen but before we do that let's skip down to work and this is where we would do something I'm gonna say do something cool here we're not gonna actually fill in that code but just imagine like this would be interfacing with a hardware device or doing something and then when we're done doing whatever we're doing we're going to emit that finished slot I should say finished signal sorry misspoke there now emit is a special keyword in Qt which is saying hey go out to signal land and emit that and then anything that's connected to it will say oh something happened finish was emitted and we need to use whatever slot is connected all right back up and run this is where the heavy lifting is going to take place here I'm gonna grab this and this is where we need to slow down a little bit for this section what we're going to do is we're going to implement an event loop now this takes a little bit of explaining so I'm going to explain as we go I'm just going to say we are starting the loop now when we say a loop we're not actually going to loop repetitively this isn't like a for loop or a do loop this is strictly inside of the event system inside of Qt so we're gonna say Q scoped pointer meaning we're doing memory management now and we're gonna say Q event loop there don't get hung up on the details there's a billion different ways of doing this I'm just showing you what I call the lazy person's guide to doing this because I absolutely hate memory management I would rather the computer do it for me so all we're doing here is we're making a scoped pointer using an event loop now notice this is a scoped pointer so when this function is done running Q scoped pointer is going to automatically delete that for us we are also going to make a timer there's a few reasons why we're doing pointers here well the first one is we are going to use this class in a thread and if it is already existing it simply won't work you're gonna get a cross thread operation and what do I mean by that if I go into the header and say something like this private Q timer when I go to use this timer it's not going to work if we've already created it on another thread because remember the worker in the instance of this would be created on the main thread and then we would move it to another thread but when it's created the timer would be created with it so that is why when you're working with threads you want to create things pointers as needed that way they're created in the thread which they actually need to be running on alright a lot of explaining there sorry about that but anyways we have two scope pointers an event loop and a timer so the timer is going to well do what timers do which is a specific interval it's going to do something so I'm a timer dot set I am the victim of my own horrible spelling there we go timer dot set interval and we're gonna set this to two seconds which is an eternity in computer time and now we're going to do is we're going to do some connect code and I hate typing connect code so bear with me there's probably not an easier way of doing this unless we do auto connections which we'll cover later but it just is what it is for the time being so we're gonna say Q timer time out and in Q's defense this is probably way better than it used to be in older versions of Q so so what we're doing here is we're saying when the timer fires off the timeout is emitted our class is going to call work so every time this timer fires off work is going to be called now what is going on here timer data that's why in case you're wondering what was going on I was using the operator this guy and we're not working with a pointer we're working with a local resource the Q scope pointer data points to the actual pointer which is a pointer to the Q timer out on memory if that's not blatantly confusing I don't know what is alright so basically when the timer fires off home we're going to call this work function now we can just use this to copy and paste my favorite invention in the whole wide world and we're going to do some surgery here and say this meaning this instance and we're going to say worker finished so when the worker emits a finish down here and work right here then we need to do something here and this is where it gets a little bit confusing what we're going to do is we're going to say this loop remember we want the data to go to the actual pointer otherwise it's going to be pointing to the Q scope pointer and we're going to say Q event loop quit meaning we're going to tell the event loop which we haven't really used yet to quit we're setting up our signals and slots so we don't have to worry about it it's just going to automatically work for us all right now we're going to go ahead and start our timer now you may be asking wait a minute that's a function and that's a slot I can tell by the icons what's the difference well honestly other than the signature there's really no difference you're calling it and it could be called as a slot or as a function it really doesn't matter now we're going to use our event loop so we're going to say loop dot exec and what loop exec is going to do is going to block our current thread so let's highlight this enough one and it says enters the main event loop and waits until exit is called returns the value meaning this is now going to block so whatever thread it's running on it's going to come to a screeching halt and nothing else will get processed we're doing this in this example because the timer is going to be firing away asynchronously in the background and it's going to do something cool like connect to a hardware device after two seconds and then when it's done it's going to emit finished so we have to have this guy right here to tell the loop to quit otherwise it's just going to block it forever now as you can guess blocking is very bad so you don't want to do that on the main loop I should say the main thread geez too many loops you know what I meant all right now that we've got this let's go ahead and give the class a good build all right now realize this class is super super confusing quick recap here this is a Q object so we can work with signals and slots it's implementing runnable so we have to have the run function this is called in a thread pool but we can also use it out of a thread pool just as a normal function or even as well however we want then we have a public slot called work inside of this worker we're going to monitor the constructor the deconstructor and then we've implemented run this is just going to start a loop and a timer the timer is going to fire off every two seconds but really only use that one time if you're paying attention here because what's going to happen is when it emits finished our class emits finished our event loop is going to quit so we're going to stop blocking speaking of the event loop what this does it basically runs a loop in the background and that loop really blocks our thread you have to be very very careful and it says generally speaking and no user interaction can take place before calling exact as a special case modal widgets like you message box can be used calling exact if that sounds very cryptic like you don't know what I'm talking about because we haven't covered any of it but what it's really saying here is you have to be very careful using this because you're going to start blocking the execution of your application and bad things happen very very quickly all right let's flip back into our main and we are going to just pop in some includes out of my notes don't know why I have it like that but we're going to do it the correct way all right now that we've got that in there we're going to you guessed it set up for the rest of this video which is our thread current thread we're going to set that object name to the main thread and then we're going to just say hey we're finished and then in the coming sections we're going to go over the different ways of doing it so at a very high level all we have so far is well nothing we just have a class it's we're going to be able to work with threads and if you run it at this point it's just going to say it's finished on the main thread major takeaway here is what we're going to do is take this worker class and bounce it around the different thread strategies and see how it acts all right to start off with let's run this in the main thread you may be going no wait a minute I thought this was a video on threading yes I'm going to show you what people do wrong that way you can avoid doing it yourself because trust me I've done it many many times first thing we're going to do here is we're going to make a function called test main and in here we're just going to slap in hey we're going to be testing this in our main and we're going to plop it between here so we can see it actually run all right back up here let's go ahead and get this going I say worker and call this worker notice right off the bat what we're doing here we have a pointer and well I need the new keyword for it to actually be a pointer this means we have some memory management we have to deal with otherwise we're going to have a memory leak and you can see yep now I was kind of waiting on the IDE to catch up yep it's saying you know potential memory leak blah blah blah so we are now in memory land and we have to be very mindful of that and we're going to say worker and we're just going to call run you may be going now isn't run part of Q runnable it is but we can still call it directly because it's just simply a function and then worker dot delete later there's a bunch of different ways you could have done this you could have just deleted it directly but we're going to tell Q to garbage collect that by using the delete later function and let's go ahead and say Q info horrible spelling today don't know why maybe I need more coffee that's usually the answer here all right let's go ahead save and run and see this thing in action remember this is running on our main thread all right so what we've got here testing on main our worker with its memory address you can see there's the constructor running on main thread then we have our worker run running on the main thread this is bad because remember that little event loops running in the background and that will have a ripple effect through our application because everything is running in the main thread and it will lock that up so if you see here it says work is called on the main thread and then finished and then testing finished blah blah and then our deconstructors finally called meaning that was garbage collected but let me really demonstrate what I mean here let's set this out to some astronomical value yikes that's a long time to wait so we're gonna save and let's rerun this and see this now you can see what I mean here it's starting but nothing else has happened because that event loop has locked up our main thread and if we wanted to wait that full minute then you'd see everything pop out but let's go ahead and close that and let's dial that time back to five seconds five seconds I even when I'm recording I feel like that's an eternity but we're just gonna sit here for five seconds so how are you doing I'm doing fairly well there it goes you see what I mean though this is all running on the main thread and we blocked the execution of that main thread meaning our entire application came to a screeching halt until that timer fired off so you got to be a little bit careful when using the main thread to run your code major major takeaway here is if you're going to run on the main thread don't do anything processor intensive or that's going to really lock up your application like using a Q event loop but you noticed even though we were blocking it because we're using the Q event loop we can still get the signals and slots point in case when the timer fired off after five seconds it admitted finished and because we had that connected via signals and slots we were able to you guessed it quit that event loop now that we understand those concepts we're going to move much much faster through this video because honestly you can go back and rewatch the videos where we talked about these concepts we're gonna say test thread and as the name would allude to we are going to run this on a thread so let's go down here and comment out test main and this is where copy and paste becomes my best friend in the whole wide world so what we're gonna do here is we are going to pop in some code and if copy and paste ever betrays you like this you can simply highlight right click and then auto indent selection or hit whatever the command key is to do that so we're gonna say Q thread we're gonna make a new thread and we're going to set that object named a worker thread and then we're going to say testing on the worker thread and then our worker is going to be a new worker here so what are we doing well we're creating a whole bunch of pointers the major takeaway here is we're going to say worker dot move to thread you may be asking yourself why are you making a pointer to Q thread we'll look at where it is it's inside of a function so if we were to just make this on the stack aka not a pointer as soon as this goes out of scope it's going to kill that thread altogether it's going to say you know try to execute on a thread that was destroyed so you're gonna have a very bad time very very quickly so we're going to move to thread and we're going to move it to the thread that we created again the major thing is we're saying whatever object we want move to thread one thing we're not going to cover is actually using inheritance inside of a thread I don't do that because I've seen people make some custom thread class and then wonder why their application blows up later so don't do that use move to thread which is the way it was intended alright so from here just going to copy and paste a whole boatload of connections and if you're lazy like me you can format it manually there we go so we've got connect and we're connecting our thread started to our worker run so we're just calling that run function basically we're saying worker when it's finished the worker is going to call delete later wait what so that means the worker is going to queue itself up with cute saying hey go ahead and delete me later I'm not needed anymore we're going to do the same thing with the thread when the thread says hey I'm finished we're going to delete later meaning we're queuing those up for garbage collection alright now our worker when it's finished we're going to say thread quit notice there's an order I'm doing this it doesn't matter with the connection code because the events in which they're admitted I should say the order in which they're admitted is really going to define this but I put it on the screen in this order the workers going to run when the workers finished it's going to say hey cute delete me later now when the workers finished it's going to say the thread needs to quit which is going to tell the thread internally to go and shut down the thread eventually is going to admit it finished and then we're telling hey when the threads finished go ahead and queue that up for garbage collection as well this is what I mean by you know connection code can get very cumbersome not just in typing it but just figuring out what in the heck is going on here so this is something I absolutely love and hate about cute is that it can get very verbose very quickly but once you get it down it's just amazing so so we're going to go ahead and start that thread and then I'm just going to say cute info so what are the pros and cons of this before we actually run this well the pros are it's now going to be threaded it's not going to run in the main running in Maine had no pros and all the cons but now that we're in a thread by using the move to thread it will be a truly multi-threaded application but you see the complexity just shot up through the roof we have all these connections we need to figure out we need to make sure that we move to thread and then we have to start the thread and on top of that we have all this memory management we now have to keep track of like delete later but now it is truly multi-threaded and it should run with no problems so let's just watch it as it goes and boom it's done the main takeaway here is okay the worker was created in the main thread and then you see tested and finished in the main thread meaning the main threads are now done main threads off doing other things while now our worker is happily running on our worker thread and all of the work is being done on the worker thread pay special attention to which thread it's actually on now at the very end here you're gonna say the worker did the work the workers finished and then finally the deconstructors called meaning everything worked exactly the way we wanted it to this is good but it's not great remember we made this as a Q runnable so we can work with a thread pool now in the last section at the very end we touched on how this is a Q runnable now what does that really get us here it means we don't have to worry about all the complexity of threading we're gonna tell Q to do it for us that has a price tag though as you're about to see so I'm gonna say void test pool and let's just go ahead and comment this out and we're gonna go ahead and run that on our thread pool so what do I mean by it has a price tag to it well let's look at the pros and cons as I'm typing this out and unfortunately I can't really type and talk at the same time so bear with me but basically the thread pool has a pool of threads which we can use but it's limited in the number of threads that are rung concurrently because your computer is limited so that's one of the cons but the pro is it has a much more elegant memory design and it's much much more efficient so we get that we also have this little guy here set auto delete meaning we can tell the thread pool to deal with the memory management for us we also don't have to screw around with all those signals and slots anymore because we're gonna say a Q thread pool go ahead and you can either make your own thread pool or you can make a global instance I should say access the global intense and we're gonna just go ahead and throw this runnable out there say you go deal with this cute I've got other things to do I've got coffee to drink and I've got YouTube videos to watch so this looks on the surface amazing because our complexity just went way down but we've gained the the awesomeness that is multi-threading now what do you think's gonna happen here do you think this is gonna run perfectly no remember there is a con and we're about to see what that con is we're gonna run this really really fast no pun intended because a threaded application all right so you can see right off the bat here main thread boom workers created on the main thread testing finished main thread now thinks it's done the worker was run on the threaded pool this is the thread inside the thread pool and then it's starting on the thread pool and then it's working on the main thread and then wait what did you catch that worker work is running on the main thread even though this is running in a thread pool what what the heck is going on here so to finish this up it's finishing on the thread pool and then it's deconstructing on the thread pool but why is work being called on the main thread this is one of the complexities of multi-threading so let's jump into our worker and figure out what's going on so looking at this what is actually calling this well our timer set interval so when that timer fires off timer time out it's calling work remember the timer is asynchronous not actually threaded so the timer is going to do that wherever Qt's going to tell it to do it and in this case Qt is telling it to fire that off on the main thread but this is the thing that's going to be doing all the number crunching that we don't want to lock our application up for so this is where you have to really dive into the documentation and understand the connection type by default it's the auto connection type if you're going cross-thread you want a Qt connection which is what auto connection is supposed to do in this case auto connection is betraying us we want a direct connection and feel free to just highlight that and jump into the documentation direct connection the slot is invoked immediately when the signal is emitted the slot is executed in the signaling thread meaning we're not going to jump bounce back and forth between threads where Qt connection well this would return control the event live the slot is executed in the receivers thread so we want it in the same thread so we want the direct connection let's try that again and see if it actually works this time and when it times out bang there we go now you can see that work or work is actually doing it on the thread in the pool not on the main thread this is what I mean by you got to be a little bit careful when you think that you know thread pool is going to solve all your problems because it will do some weird things in the background just bear in mind that if you're going to start working with you know pointers out in your run function you're going to want to make sure that those signals and slots are firing off in the same thread yes you may have actually wondered no wait a minute does that mean the event loop is running on the main thread yes it is you have to be very careful with this so just to kind of proof of concept here let's just flip both of these over to direct connection just to make sure this still works as advertised and when it times out bang now everything works the way you would expect it to work so our worker is created in the main thread our main thread is done and it thinks it's ready to go off and do other things and all of the actual work is now truly being done on the thread pool all right we've covered a lot of things so far here we've covered running in main bad idea we've covered running in a thread which is well good because it makes us multi-threaded but it's bad because there's a lot of things we have to type thread pools are awesome but of course now you have to deal with all these weird memory issues what we're going to cover now is cube concurrent now remember this is a high level API one API to rule them all but it does all the threading for us and to that end we have to treat it a little bit differently here we can't just call test concurrent directly like this because then what are we doing we're really running this in the main thread so what we need to do here is say qt concurrent run and then tell it what function to actually run if you can't get to that point remember you need your include and in your cmake list you also have to include concurrent and it has to have all of that in there or it just won't work and when we're running it's basically going to take this entire function wrap it inside a runnable and throw it out on a thread pool at least that's how I imagine it happens under the hood I honestly don't know for sure but logic would say that's how it's working one of these days I'm going to get out to the source code and check it out just to make sure I'm saying that accurately because that's the awesome thing about qt is you can look at the source code in case you're wondering you just got to google and type in qt source code and there it is all right so we're going to call this worker thanks spell worker you'll notice right off the bat this is a scope pointer meaning wait a minute as soon as this goes out of scope it's going to delete this that's absolutely right this is what I mean by you have to treat this a little bit differently now we can use smart pointers and use a multi-threaded design and a thread pool all combined into one and we don't have to worry about any of the things but because this is working at a higher level we also lose what's called granularity meaning we can't really control all the nitty gritty of things we just trust qt to make it happen and let's go ahead and say testing all right so this is much much simpler compared to the other designs the big takeaway here is well this entire function will be run on a thread and now we can work with scope directly we don't have to worry about delete later and all that other fun stuff let's go ahead and run this using qt concurrent run and see what happens here and after five seconds bang there we go so main thread and then testing and then our workers made on the thread pooled this is cute concurrent throwing it out on a thread pool workers running on the thread pool starting on the thread pool work is done on the thread pool finished on the thread pool and then you see where it says testing and then finally our workers deconstructed you notice the difference there it's not deconstructed and then testing or any other thing it's it gets to the end here and then when it goes out of scope that scope pointer deletes this automatically for us all right so quick recap here we've got run on main run on a thread pool and qt concurrent which one is right which one is the best well there really isn't a best that's why all of these methods exist however i tend to lean towards using qt concurrent because as you can see compared to the others it takes a lot of the complexity and guesswork out of this it's simply very elegant and very easy to use all right as promised we're going to look at very simple simple widgets example because all we've been doing so far is on the command line but understand there's a reason why when you're looking at the command line that you're like what we do it's a threaded application it all looks the same but if we go to new project and say cute widgets you're gonna notice all these other things and don't worry we're gonna cover these in future videos so don't get overwhelmed with questions but we're gonna say cute widgets and then let's just call this test cmake you know how it's not much different to get to this screen there's a main window a widget and dialogue i'm just gonna make it as a dialogue so it's much simpler it's going to give us a header a source file a form file which is actually just xml under the hood and we want to generate that form and then the rest is pretty much the same and let's look at the structure of this real quick here so you can see how we have headers we have source files and then we have this dialogue dot ui now in our main we have a q application and a dialogue w dialogue show what is going on here well basically it's making an instance of this dialogue which here's the code underneath it don't get super confused we're going to go into this and it's basically building up a copy of this xml file i know this doesn't look like xml because this is the interface editor or i should say that what you see is what you get at her and what we can do here is we can just drag and drop visual elements like i can say i want that button on here right and we can do things like you know i want it to look like this and if i'm whipping through this and you're going now wait what are you doing i'm going to cover all this in future videos but i want to ask you and please leave me some feedback in the comments below do you want me to ditch the command line and start working with widgets or do you want me to continue on the command line and then once we've covered like networking and other things go over to widgets just drop a comment below but everything that i'm covering and i i hate shameless advertising so forgive me but it's out on you to me under the where is it cute five widgets for beginners covers way way way more than you ever want to know about widgets just for starting off but all we've done is we've dragged and dropped a button in here now if we close this i want to show you here it opens it back up and it puts it all just beautifully if i go to edit you can see the xml properties under here we're making a q push button and then we can do things with it so we're going to go back into our dialogue highlighter button and then we're going to go to slot down here and then we're going to say when the button is clicked it okay and then we can do things with it and it's actually really really cool and you notice how what's going on here is it's adding it in to our dialogue so i'm saying include q thread and i'm going to show you why threading is super important in a user interface occasion here because let's just say we were to do something user or i should say work intensive here i'm going to do something like this q thread current thread ms sleep and we're going to put this to sleep for a very very long time one minute that's an eternity that's like the entire existence of the universe and all the things in computer time here but let's flip back to our main real quick i just want to demonstrate something here so what's going on here is you guessed it the dialogue is running in our main thread and to really solidify that in your mind we're going to do this this is the one thing that i really don't like about which it's programming is well all of your user interface runs in you guessed it the main thread oh that is not good right so let's go back in here and we're just going to print that out now again don't get caught up in the complexity of what is all of this stuff i'm pretty please explain all this we're going to get to it just not in this video again leave a comment below whether you want me to ditch the command line and go into widgets programming or if you just enjoy the command line so what we're going to do now is i'm going to demonstrate why threading is super important to understand before you even attempt user interfaces so we're going to say q info and we want to know the q thread current thread and then whatever the current thread is we're going to put it to sleep for a very long time save and run now this i'm going to warn you is going to act very different on different machines and you see i have this terminal in the background you can get rid of this by choosing projects and how it's running we'll cover that in a different video but if i click this you see it's running in the main thread and now our our program we can't do anything i can't even close this it's hung up see what i mean it's like a dialogue not responding force quit i'm sure you've seen applications do that before so what we've done is we've just completely hung up our main thread and effectively killed our application not cool but that's the biggest reason right there why you want to really master threading before you move on to other topics i hope you enjoyed this video you can find the source code out on github.com if you need additional help myself and thousands of other developers are hanging out in the void realms facebook group this is a large group with lots of developers and we talk about everything technology related not just the technology that you just watched and if you want official training i do develop courses out on udemy.com this is official classroom style training if you go out there and the course you're looking for is just simply not there drop me a note i'm either working on it or i will actually develop it i will put a link down below for all three of those and as always help me help you smash that like and subscribe button the more popular these videos become the more i'll create and publish out on youtube thank you for watching