 Welcome back everybody, my name is Brian and we're going to continue on our conversation about cute threading. In the last video we did something like this, well we have an application and we made a thread and we put an instance of a class on that thread. That lived inside of our app and we made a very simple threaded application. The problem is now we're going to make things a little bit more complex. In this video we're going to make, and let's just do a little copy and paste here, we're going to make another thread inside of our application and another instance of a class and everything's going to seem perfectly fine until we want these two instances to share information back and forth. So we're going to do something like this. Now this problem on paper looks extremely simple but you'd be surprised how complex people make their solutions and people have tried to solve this over and over again in the history of programming. Cute however makes this ridiculously simple. We're going to use signals and slots. There's a couple little caveats we got an old way, so let's dive in and take a look. If you already know signals and slots, this section may be a little bit dull but I promise you there are a few little things you got to watch out for. Use the buffer bar below to skip ahead where you need to. First thing we're going to do is we're going to add a new class and we're going to call this worker and again I do talk and type at the same time. This is not like a record over or a voice dubbing or anything like that so please forgive any typing mistakes. We're going to include Q object and make sure we have this Q object macro. If you forgot what these do, include Q object will actually earn this into a Q object where the add Q underscore object macro makes sure that signals and slots work in the background. That's critical for this video because we're going to be working with signals and slots. Hit next and finish. Now again I'm using Q6 with CMake so you're going to have to go in and do a little brain surgery on this. I'm going to say worker and I did have a question, does it really matter if you have the implementation file first or the header file first? No, it really doesn't. CMake is smart enough to figure it out when it builds it. Let's go ahead and jump into our worker here and there'll be a little bit of copy and paste action here just for the sake of time. We're going to use Q object, Q thread, Q debug, Q timer and Q random generator. Real real quick review here. Q thread allows us to work with different threads. Q debug allows us to output things on the screen. Q timer, well this allows us to asynchronously use a timer. You don't know what a timer is, count down in your head from 5 to 0. That's exactly what a timer does. It just does something and you can do it either once or on an interval and then random generator is well pretty self-explanatory, it's just going to generate a random number for us. Now there's going to be a whole boatload of copy and paste here. Brace yourself. Let's go ahead and just, this and blah, that's the official term for the day, blah. We have a worker, which is our class. We have a getter, a setter, or R, whether or not we're a producer. Now if you're wondering how I got these, look down here, I have a bull called M producer. I'll show you a little trick, wipe those out. You can just simply highlight that, factor and then you can either generate a setter, a getter or generate both and voila. Click it, it just magically creates the code for you. I absolutely love that. Now some other takeaways here is we have some signals. We produced where we're going to say, hey, we produced some sort of value. Finished is going to let the other class know, hey, we're done producing and then some slots. Notice these are public slots, which means they're available outside of our class. Yes, you can actually restrict these to public, private, active. We've got consume. Consume is going to take the value that was emitted from the produce signal and consume it. So it's just going to take this random number we're going to generate, emit it and then consume it on the other instance. And then we have start and quit. Start is going to be called when we start on the thread. Remember, we cannot start in our constructor. We have to wait for the thread to start us. Quit is just pretty self-explanatory. We're just going to close this object down. And we've got some private stuff here. You timer, notice this is a pointer. If you do not make that a pointer, if you just make it like this, you're going to get this error right here. Timers cannot be started from a thread. Because when this class is created, I should say when it's initialized, it's going to be created in memory on the main thread. And we're going to move it to the producer thread. You'll see that in the next section here. So let's just put that back as a pointer. And we've got a bull indicating whether this is a producer or a consumer. So we're going to have two instances of this worker. One is going to produce things, whereas it produce, and the other is going to consume things. So one's going to generate a number, and one's going to use that number. And we're only going to do that a certain number of times. So let's go ahead and flesh this out. Now, newbies are often surprised when they go into the signal section, and they right click, and they go to Refractor, and nothing's there. You don't actually implement that. Go down to your slots, and we're going to right click and assume, add definition in worker. Again, I did say if you're already familiar with Qt, this might be a little bit boring. All right, we've got the bulk of our code right there. Let's give this a good little build, make sure this guy's happy. All right, so far so good. Now, what I like doing is right here in the constructor. Yes, there are other ways of initializing these, but I like doing it right in the constructor, just so there's no question what's going on. Also could have done this down here when we started on the thread. Either way, works doesn't really matter which way you do it for this little example. We've got our getter, we've got our setter, and now we've got our consume. You may be wondering, wait a minute, where's produce? Well, we're going to produce on a timer. So let's fill out start first, just so you understand what I'm talking about. I'm going to grab some code off the screen, do the little copy and paste. So we're going to say Q info, this current object is starting, and then we're going to print out the thread we're currently on. Now we need to create our timer. Remember, this is a pointer, this is a new Q timer, and we're going to give it a parent. Now you may be asking yourself, if you watched the last video, now wait a minute, isn't it true if you move a class or an object to a thread, it cannot have a parent? That's absolutely true. However, we are creating this after the thread has started it. So it's not going to have a cross thread operation. Remember, if you try to just make this as a member variable like that, not a pointer, well, you're going to have a bad time and you're going to get this error right here. Timers cannot be started from another. So that's one little gotcha you'll have to work out, is that anything that you want to create in the class, you'll have to create as a pointer and as needed. Okay, fun time. What we're going to do now is we're going to connect up our timer. So we're going to say connect, and if we look at our timer, we need something to connect to. If you're ever curious what's going on, highlight the class, hit F1 and it brings up the help file for us. We can simply scroll down and you can see everything you need from properties to functions and ha ha, public slots and signals. So the signal the timer is going to emit is time out. So here is the signature right here. We need something that matches that. I'm just going to close that and let's go back into our worker here. And do we have a timeout? We do. So let's right click this refractor add definition. Bang. Now it's going to be very, very simple. We're going to connect up our timer. Let's go ahead and say M timer. Remember, we're doing the signal I'm out to our slot. They have to have the same signature. You're going to get some sort of weird error. But basically every time this timer fires off, it's going to call this because we've connected the signal and slot. This is all happening in the same thread. Timers and Q are asynchronous. And asynchronous means it all happens on the same thread. We don't have to worry about any cross thread operation there. It's actually pretty cool. Now go back to our Gitter and Citter. Remember, one of these is going to be a producer, one of them is going to be a consumer. We're using this little bull here. So let's go down here. And we want to go ahead and say if we are a producer, then we're going to, well, produce something. Grab him. Just going to put some text on the screen just so we know what's going on. We're going to say we are starting the timer. And we're going to take our timer. And let's go ahead and configure it first. We're going to say timer set interval. And we're going to do this every second. We have to give it in milliseconds. So it's 1000 milliseconds is one second. Then we're going to go ahead and start our timer. There we go. Now you may have noticed while I was typing that I call the slot start. It really doesn't matter because remember, slots and functions are identical. A slot is just a function with cute mock on top of it. Meta object compiler comes in and adds all this goodness for us. So we don't have to worry about it. That's it. It's really that simple. So looking at this, when our thread starts, we're going to say this object is starting on whatever thread. We're going to make a new timer on this thread on this object. We're going to connect our timer timeout to whatever object we're on timeout. This guy down here. And then we're going to say if we're the producer, go ahead and do something. And we're going to say timer set interval 1000. M timer start. And it's just going to fire off and get going here. It's actually pretty cool the way this works. Now let's go ahead and take a look at quit. But as we're going to say, well, I'm just going to copy and paste. It's probably easier just to do that. Wham. So we're going to say this object is quitting and it's on the current thread. Now we want to go ahead and stop our timer. Now you may be asking yourself, but what if the timer is not running because only produces running? It's going to have zero impact. We're not really worried. Let's go ahead and emit finished. Just to let anybody who's connected to us know that, hey, we're done. We're not going to work anymore. Consumer, let's just switch modes here and say we're the consumer now, not producer, and we want to consume that number. Actually, this example is going to be very minimal. We're just going to say, hey, we're doing something with it. So we're going to say Q info. This object is consuming whatever value we're given on the current thread. Remember, these are going to live on two different threads. Even though this is one class, we're going to make two instances of this class that will run two completely different threads. We are almost done. Let's go down to our timeout here. This is the interesting bit. Remember, the producers got the timer that's firing in the background here. This thing's just going to fire off forever until we tell it to stop firing. So we're going to say int. Well, first thing we're going to do here is let's switch gears. We're going to create our random value. So we're going to say Q a random generator. I want the global instance bounded. And if I'm going too fast, fear not, all of this information, remember, is in the help system. If you ever in Qt Creator don't know what something is, just highlight it, hit F1. It'll take you right to the help file, and you can read all of the documentation. And sometimes there is simply a lot of it. Because I'm scrolling, you see how big the Q random class is. Really, all we want to do is get the random generator, use the global instance, and bounded is going to say up to a certain limit. See, it generates one number between a range. And I'm just giving it the highest range. All right, now that we've got our value here, say we are producing this value on this thread. So we're producing, we're consuming. These are going to be two different instances that are going to use these two functions here. Let's clap some of this stuff down, just so it's not super confusing. So you have, whoops, lapsed way too much. There we go. So we have consume, we'll be on one thread, and then our timeout, which is actually producing on another thread. Extremely important you understand that concept. Now that we've created this value on our producer, we need to get this value over to the consumer. We do that by a signals and slots. We're going to show those in the next section, but this is how it's done. We're going to emit a signal. We're going to say we have produced. And if ever you're in Qt and you don't know what's what, you're looking at this, remember these little like Wi-Fi bars means it's a signal. So we're going to emit our value. And it's going to copy that. And it's going to shove it out into the ether, and whatever's connected to this object, we'll get called by Qt internally. That's part of Mach, that object compiler. All right, now let's go ahead and increment account here. And we're going to say if our count is equal to five, then we want to go ahead and quit. And remember what quit is going to do is we're going to say we are quitting on the current thread. We're going to stop the timer if it's running, and we're going to emit the finished signal. Now you may be wondering, emit, what is this really doing? Well, it's telling Qt that, hey, this event is happening, and any other object that is connected to it is going to consume that. That's why I do this producer consumer example first off. So you really understand what's happening with signals and slots. Pretty simple, pretty easy to understand, but sometimes you get those newbie questions. For example, in our connect for our timer, when the timer times out, the worker will get called timeout. Again, timeout is emitted from the timer, the worker calls, or I should say Qt calls, the timeout slot. Seems super confusing, but just remember you have a cause and you have effect, emit, and then you have a slot. I should say a signal is emitted, and then you have a slot that's being called. Again, super confusing for newbies, but really it's just cause and effect. Okay, just in case you skipped ahead, what we're going to do now is we're going to take this worker class that we just created, we're going to make two instances. One's going to be a producer, one's going to be a consumer. We do that via a getter and setter. I should say the setter more appropriately here. So let's go ahead and I'm going to slow way down this area, simply because this is the meat of the tutorial here. So let's jump up here and we're going to make two threads. Two thread, and let's call this P thread. This is going to be the producer thread, and then Q thread, and this is C thread, and this is the consumer thread. Remember, we have two different threads, but now we need to make two instances of our worker class. Let's call this producer, and then worker consumer. At this point, you may be asking, wait a minute, do these need to be pointers? Really they don't, because it's all going to be handled on the stack and C++ internally will handle that memory for us. Can make them pointers if you want, but then you will have to manage that memory. Now what I want to do is we want to move these objects, the two instances, to their appropriate threads. So I'm going to say producer, move to thread, and we're going to move this to our, you guessed it, P thread. We're going to do the exact same thing with consumer. We're just going to literally just copy this, paste it, just to show you how ridiculously easy this is. Once you get the pattern down, the producer is going to the P thread, consumer is going to the C thread. Consumer, consumer, two distinctly different objects. Now remember, in our worker, we have that getter and setter, which defines if it's a producer or not. So I'm going to grab producer, and we're going to say set producer is now true. So only one of these is going to be the producer. The other one's going to be obviously the consumer. Then I have what I call some, well, well, I don't know what to call it, plumbing code. We're just going to pretty this up a little bit. So P thread, set object name to producer thread, C thread, consumer thread, producer to producer and consumer to consumer. That way on the screen, you don't just see memory addresses, you see actual names. Just wanted to copy and paste that just to get that out of there. Normally, I absolutely hate typing Q object connect, but I'm going to do it just because you really need to understand what's going on here. So I'm going to say Q object connect and kudos to Q. I mean, this is pretty complex stuff, and it's gotten a lot better than it used to be, but still, I absolutely hate this part. There's arguably probably not an easier way to do this other than auto connect, which we will talk about in future tutorials. So what we're going to do is we're going to take our producer thread and we're going to say Q thread. And we want to get the started signal. Remember, you can always highlight, hit F1, and then go through the help file and figure out what's going on here. It's got public slots. It's got signals started and finished. So we're grabbing started, and we're just going to kick off this thread. I'm going to say close that. When the producer thread is started, what we want to do, probably already guess what we're going to do here is we're going to take that producer and we are going to start that. And again, if you're wondering what a signal is, it's got like this little, I don't know, it looks like an outlet almost, like you can plug something into it. That is the icon for a slot right there. So you want to do a signal to a slot. Sometimes the IDE will do this little thing where it thinks it's helping us and you just got to back that out. All right. Now that we've got that general idea down, we're taking our producer thread, starting it to our producer start. And if we look in here, go to start, you can see this is the bulk of our application. This is where we're saying, hey, make a new timer. If we're producing something, start that timer, take it off back here. Now I'm going to do the old copy pace, grab that bang, and we're going to say C thread started to the consumer start. Now remember the consumer will not have this set producer true. Therefore it will not start firing off a timer. Now we need to connect up these two objects. We've got two different objects living on two different threads. We need to connect them together. So I'm going to wipe this out. I'm going to say producer and we've got a few signals here. So we want to say when it's produced, we're going to go to our consumer and we want to go ahead and consume that. See, there is our little slot for consume. We can tell by the icon. Again, the IDE is trying its best. Sometimes it acts like Internet Explorer, but I love it to death. There we go. So now we're saying producer is produced, consumer is going to consume, and then let's go ahead and grab this. One more, we're going to say the producer is finished. Then we're going to head and tell the consumer to go ahead and quit. That way we just shut those both down. We are almost done here. What we need to do is see thread. We're going to start our consumer first, because remember that producer is going to kick off and just start going. I'm going to start this. Under the hood, there's really virtually no guarantee which order they're going to go, but usually it works just fine. This is a very simple example. We're going to dive into the more complex stuff later on, just trying to get you going in the right direction here. All right, let's give this a good build. Make sure we don't have any gremlins. And we've got a successful build. We're good to test. Okay, moment of truth. Crush your fingers. Let's see what happens here. Let's go ahead and run this. And remember, while this is running, we have two different objects on two different threads. And I'm just going to explode this all the way across the screen. All right, we have our consumer starting on our consumer thread, our producer starting on our producer thread. So now we have two different objects on two different threads. Producer is going to start the timer on the producer thread. Remember, timers in Qt are asynchronous. They run on the same thread. And then you have the producer is producing this random value of 251. Consumer is now consuming that value of 251. So now you have two different objects on two different threads that are now communicating. And it's doing that via signals and slots. This couldn't get any simpler. Please do not reinvent the wheel. Don't make some crazy memory-shared allocation thing. Don't do that. Use signals and slots. That's what this is designed for. And you can see it just works perfectly. You have this delicate dance of the producer-consumer over and over and over until we hit our max. Then the producer says, you know what? I'm done quitting. It sends that signal over to the consumer who says, okay, I'm also quitting. And they're both just finished. This is absolutely awesome. And I love one major takeaway, just in case you're coming from Qt 5 and you're watching this video as a review, you'll notice something. When I do this connection code, I'm using the auto connect. And if you're wondering what auto connect is, well, it's exactly what you're looking at. There's an extra little parameter here called the connection type. And by default, it goes to auto connection. Let's just go Qt. And that was not helpful. And we're going to go with auto connection. This is what it looks like under the hood. If we hit F1, you can see there's different types. So there's auto. If the receiver lives on the thread that emits a signal, direct connection is used, meaning it goes directly to that object. Otherwise, if they're on two different threads, it's going to use a Qt connection. Interesting. So let's flip down to Qt connection because these are on two different threads. The slot is invoked when control returns the event loop of the receiver's thread. The slots is executed in the receiver's thread. Wait, what? What this means is if they're on two different threads, it's going to put that signal's information into a Q somewhere. That's why it's called a Qt connection. And it's going to wait until the receiving object is ready to receive it. Because remember, it's on a different thread doing its own thing. As far as it's concerned, it lives in a completely different universe. Once it's ready, then Qt is smart enough to say, hey, here's all the information you've been waiting for, and it hands it to it very delicately. You no longer have to use the Qt connection. If you are coming from Qt 5, you're used to doing this. If you had a cross thread operation, and anything that was cross thread would have to be a Qt connection because it would more than likely just not work without it. But starting with Qt 6, you can use the auto connection, and it works just fine as you can see here. 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 Voidrealms 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.