 Hey everybody, welcome back. This is Brian and in this video we're going to talk about a complex but yet common problem and a simple solution. So the problem first off is well sharing information or specifically resources between threads, meaning you have multiple threads in your application and one or more of them is trying to update that at the same time. So you have this thread and this thread trying to update this resource and let's just call this an integer. So let's say this thread is going to minus one and this thread is going to add one. Seems simple enough until you think about how a computer works. What if both of these hit at the same time? Well in your mind you would say well they negate each other right? So minus one plus one it would just there'd be no change. That's not really how a computer works. Usually what happens is the computer gets very confused and well does some goofy number like this or more appropriately I'm sure you've seen this before. That being said a lot of people will try to solve this in a number of different ways but we're going to show you probably the best way of doing this. I don't like this design but this is what people tend to do. They go okay I know what we're going to do. We're going to use a thread pool and we're going to pull these threads from the thread pool but look what we're doing. We're not actually solving the problem. We still have the same thing just now we have a thread pool. I do cover this problem and the solution in-depth in my course Qt6coreAdvance for C++ available out on udemy.com. Be sure to check it out. Now let's take a look at the actual solution to this. So how do we solve this? Looking at this problem we have a pool with multiple threads and we want to update this value I should say this variable. The shared resource unfortunately needs something in between so just having these threads hit it directly is a lot like having a stoplight that is not lit and as you can guess bad things usually happen. So we need to put something between these so let's go ahead and grab this and we're going to use what's called a mutex or specifically in Qt terms it's a Qmutex we're going to use a Qmutex locker and what this does is it acts as a stoplight between the threads trying to update this variable. That way they know whether well just to illustrate the point they can go or they have to wait and basically it makes them go one at a time. This sounds counterintuitive because you're going to think wait a minute I thought the point of multiple threads was to be able to do multiple things at once. That is true unfortunately if you try to update the shared resource multiple times at the same time you're going to get corrupt data. So you need this stoplight if you will to kind of act as like a gatekeeper to determine which thread can actually update this. Let's go ahead and take a look at how we would implement this. Because we're going to be working with a thread pool we need to implement a runnable so let's go ahead and make a new class I'm going to say add new class and let's call this worker and this is where a lot of people are going to get really feisty down in the comments. I'm going to make this a base class of Q object but you don't need to. I'm just doing this so in the future not this video if I wanted to work with signals and slots I already have Q object and all its goodness baked right in. Now the reason why we're not going to be working with signals and slots is because well signals and slots are going to do one of two things either you're going to make a copy which defeats the point of this video meaning you would make a copy of that value which doesn't help because it's not a shared resource or you would share a pointer using signals and slots which puts you right back to the problem statement we had at the very beginning where you still have two objects or I should say two or more objects try and update the same thing at the same time. All right so we're going to put some includes in here we've talked about these before thread runnable we need to implement a runnable to run on the thread pool so I'm going to say public to runnable and I'm going to right click this refractor and then insert virtual functions of base class and we're going to go to run we've done this before some kind of weapon right through this and that's going to implement our Q runnable interface allowing us to run on a thread pool now we're going to start talking about mutex and mutex locker and let me explain so we're going to say private and we want our shared resource and this is going to be a pointer to some variable so we're going to say m count and then we need our mutex which is also going to be a pointer so what a mutex does is it acts like that stop light and it's going to tell the thread hey can you use it or do you have to sit and wait your turn kind of serializes everything if we just highlight Q mutex and hit F1 takes us to the mutex class and you can see down here we have lock try lock and unlock so really what this thing does it will lock the shared resource and say the thread and only this thread can update it and then unlock it allowing other threads to go through and you can go down here and read all about it it's actually a fairly simple little class and it's fairly elegant the problem is you have to remember to unlock otherwise all your threads will just sit and wait and here's an example you'll have a function you'll say mutex lock do something mutex unlock but if you forget to do that you're well you're in a bad place which is why mutex locker exists and the mutex locks are it's just simply a convenience class that simplifies locking and unlocking and there's a little example here same thing I just said mutex lock and unlock or you can simplify this by saying mutex locker and then when this goes out of scope it automatically unlocks think of that like a smart pointer but for you texas and we're gonna cover that here in just a second so we're going to make setters for these I'm gonna generate a center in case I went a little too fast they're gonna do same thing here we're gonna say our mutex we're just going to mouse over it right click refractor and you can generate a center puts that code in there for us so before we go to run in our code that we haven't implemented yet in Maine we're going to need to set those so in here is our problem statement run the thread pool is gonna run this code on a thread we don't get to pick and choose that thread is just going to do it for us now this can be well a little bit dangerous right now what I want to do here is say four and we're just gonna make some simple simple code say four and I equals zero I is less than 10 and I go ahead and increment again this is going to be very very simple code this is the problem and I want you to pay attention remember we're running on a thread it value equals and we're going to de-reference that pointer and this is where people in the comments are gonna say well you shouldn't do it this way you should do it that way I'm keeping this ridiculously simple so that even a C++ newbie can figure this out so what are we doing here well we're making an integer called value and we're saying it's going to equal whatever the value is at that location that's being pointed to so we're de-referencing that putting it in there and then we're going to increment now here comes the problem the problem is if we go ahead and say okay let's go back to that pointer and set it to our updated value and I'm gonna put it right there danger because we are now modifying something at a memory location and other threads could be doing this at the same time that is very bad it's very dangerous and well I'm sure you've seen applications crash before and just for giggles I'm gonna go ahead and say Q info Q thread current thread and then we're going to just print out that value just so we can see it but I also want to know when this thread is done all right now how do we fix this we've talked about mutex and mutex locker so you're inclined to go in and use a mutex which we already have down here via our setter and use it and you can you can absolutely do this you can say something like mutex lock the problem is when you do this and you forget to unlock meaning it's going to just stop and none of the other threads are going to be able to access anything beyond this point so that's bad we don't want to do that so instead we're going to use a Q mutex locker the convenience class and we're going to call this locker thank you Q creator for helping me type locker here we go I'm sure that's happened to everybody and we're going to give it our mutex so what happens now is it's going to lock and then when it goes out of scope it's going to magically unlock for us the key here is scope pay attention to scope so when does this actually go out of scope well we've created it in this for loop so every iteration of this loop it's going to go out of scope you could move this up here if you wanted to but there's really no need usually when you lock something you want to keep it locked for the shortest amount of time as possible that way other threads can go in there and access it so basically we're going to lock and unlock this every time this for loop goes through on every single thread again there'll probably be a huge debate down in the comments flipping back to our main file we're going to go ahead and implement all of this stuff that we've written so far so let's just go ahead and we're going to use Q thread Q thread pool Q mutex Q debug and our worker class that we just created remember quick recap basically we need to set our shared resource and the mutex we're going to use as our stoplight for this worker to work while it's running all right so first things first here let's go ahead and let's say and count and we're going to make this zero right up front this is our shared resource let's go ahead and set int max equals five actually let's just make it one for now now this is the number of threads that we're going to use remember the way a thread pool works if you make like a thousand threads it's not going to run a thousand threads at once it's going to recycle threads as they're available you're using a pool of threads so if you put that to some astronomical number don't expect it to run you know every single thing in the known universe so we're going to say Q mutex and this is where we're going to make our stoplight and this is going to tell the threads when they can and when they cannot update this shared resource and I should actually put this right here shared resource just so there's no confusion what that is all right now let's go ahead and say four let's say int i equals zero and we'll say i is less than max go and increment i not a big deal and let's say worker and we're going to make a new worker now you may be saying uh-oh you have the new keyword and of course it's popping up telling me uh-oh you're going to have memory leaks and all sorts of issues remember with Q we have built-in memory management I would not set a parent because we're working with multiple threads so instead what you're going to do is say worker dot set auto delete set that to true and the thread pool will delete that for us when it no longer needs it but remember in our worker we have those other setters so I'm going to say worker set and this is why naming is so important we got set out set mutex if we had named that something else we'd have to go hunting for it so let's go ahead and set our shared resource and we're going to set our mutex which our Q mutex locker is going to work with under the hood pretty simple pretty easy now what we're going to do is we're going to wait for all of this stuff to be done but first off we need to actually run this silly me so we're going to say Q thread pool let's go ahead and fire off this on the global instance we covered this in the previous video in case you're going to wait what is he doing basically we're taking that instance of the Q runnable putting it out on the global instance of our current thread pool and say run that and spoiler alert just in case you did skip the last video what it's going to do at this point it's going to put it inside an internal Q inside the thread pool and when it's ready to run it's going to run this run function that we implemented from the Q runnable interface all right almost done basically what we're going to do now is we are going to wait for all of this to get done we're going to throw a bunch of stuff out in the pool and I'm just going to say you know what I want to wait for everything to be completed and then I'm going to say Q info count because we want to know exactly what's going on here and let's just print out our shared resource now this code is very very simple so when we get to this point right here looking at this all we're doing is we're saying go ahead and increment it 10 times 0 to 10 every time we run so each thread basically is going to increment that by 10 very very simple code okay quick recap before we continue we have a thread pool with multiple threads that we do not directly control Qt controls these based off your computer settings and each one of these threads is going to be trying to update a shared resource now this can cause all sorts of issues but we have a mutex or more probably a Q mutex in between them that acts as a stoplight telling each thread when they can or cannot update that shared resource all right let's take a look at the code here first example we're just going to have one thread just to show you what it looks like when there's no contention here it's going to be very simple boom thread runs and it's going to increment it by 10 so every time the thread runs it's going to increment it by 10 let's go ahead and do five should have absolutely no problems so what's going on here is this thing's going incredibly fast but every time it goes through it it's going to lock this and then unlock it when it goes out of scope now you can if you wanted to do this manually using a Q mutex we already had that in here so let's say mutex dot lock and this is going to block our current thread until it can lock so you gotta be kind of careful there and then you can unlock that guy now this is going to act slightly different on everybody else's computer so just kind of keep that in the back of your head that operating systems do vary so don't expect it to behave exactly like this but the major takeaway here is because you have this mutex or this stoplight here you are not going to run into any sort of crashes or issues I tend to favor Q mutex locker that way I can just set it and forget it I don't have to remember to unlock it now let's take this example and just kind of make it ridiculous I'm going to say 5000 threads this computer there's no way in heck it could do 5000 continuous threads I mean let's just go to system info you can see I'm on a very very small virtual machine and if I just go to our system monitor you can see I only have one two three four virtual CPUs so this thing's not going to be able to run 5000 threads concurrently it's just not going to happen so this is the beauty of the thread pool you can do this and it'll queue things up and as those threads in the thread pool become available it will synchronize them run them and everything just works so the end result is it runs it runs multi-threaded it runs pooled and it does not crash and we can have a shared resource 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