Multithreading – Control the Flow
Hi guys. So hope you’re all doing well. In today’s video we will talk about logical flow, mainly related to multithreading. So I had some discussion via the support system with some guys about how to manage threads. A couple of days or weeks ago, I recorded that video, “How to build a state aware bots”, which was very critical. And with a state aware bot, you also have to somehow control the flow of the application. That means you control what the bot actually does or what the code does. So flow control, commands, and functions are if-then-else, loop, do-while, and stuff like that. Conditions where you change the behavior of your code, the logic of your code based on certain criterias. If there is a certain element, then you do this. If it’s not there, then you do that. So this is very, very important.
So your bot has stayed aware, it knows what to do, or better said, it knows what the current status is and what is expected. And then it changes and adjusts the behavior of the bot and the flow of your code. So when you start coding, and when you’re new to this, this is sometimes tricky. Because people start to write code like they write a letter. They start from the top to the bottom and just write the stuff in a step-by-step manner. So first you do this, then you do that, then you do that. Open the browser, load the page, click that button, go there, go there, go there, do this, extract that, save it. So that’s a very straightforward and a normal flow.
I’m not saying this is bad, but this is not proper coding, I would say. Because you do not have any control of what’s going on there and you don’t know what is happening. So of course it’s a lot more work to build a bot that is state-aware, but you can work with templates. Examples I gave for the logging and how to put certain ExBrowser commands into defines and you have logs and all that stuff. You can write that once and then copy and paste it now every time you need that. Or maybe even have a whole set of defined commands that you just paste into a new bot and then you can use that. So this is work you don’t have to do all the time.
But, it’s very important that you think about the logical flow of what you try to achieve. With logical flow. I mean when we say we want to launch two threads for example. First of all, you should not just start writing the code. You should think about what you want to happen. At what stage of the process you want certain actions to happen. So how many threads you want to start at once, how many threads you want to start in total, how you control them. How you manage the status of a thread, how you launch them, how you pause or you stop them, and all of this. The logical process of managing the different activities. So sometimes it makes sense to write that down first or even draw kind of a flow chart where you have the different connections if-then-else and that kind of stuff, without putting the real code into it.
If you want to write a data extraction tool for example, and you wanted to run multiple threads, build the logic of how you control the thread first, and then you can create the code. So this is very important that you have a logical understanding of what the stuff should do. So you’re not going to write the regular step-by-step code and then just easily paste it into some multithreading code that you found somewhere online or in the forum. Multithreading is not easy and you should really invest some time and understand how that works and how to properly design that, and give that some time. So I will show you some examples. And sometimes it even makes sense to write little test code to just understand what is happening, because to be honest, Ubot is sometimes a little bit strange and stuff changed during the different versions.
So let’s take a look. I have a couple of simple examples for you. So what do we have here in the test code? We have two stat monitors. One and two. We start with a variable set to one. Already run that, so let’s reset that. Then we have one thread that runs a loop. 20 cycles with a half a second pause in between that increments that counter. Then we have one-second pause. Then we launch another thread that has two loops.
So what I want to show you next is some ideas how to stop the thread and some stuff, what to not do. So this is pretty straightforward. 20 threads here. 20 threads there. So both counters should count up to 20. And the second counter is a little bit behind because it starts one second later. So let’s run that. As you can see it goes up. They go up. Nothing happens. Can scroll around. To 21. Okay, 20 plus one is 21 so that’s basically it.
So now what you will want to do. At some point, the threads run independent from that regular code flow. So they launch and then they execute the internal thread code in their own thread, what the name says. And then you can execute something after that. So there’s one thing that most of you are probably aware of, but I just want to remind all of you.
Because in the past, stop script had a different behavior than it has today. Because stop script only stopped the flow in that current thread. That’s not the case anymore. Stop script stops the flow in the main thread and in all threads that are executed from that main thread. So that means normally in the past, if I have that code, that stop script at the end would not do anything to both of those threads. They would still run in the past. It’s a couple of months or years ago already. I just want to remind you that sometimes stuff changes and that’s why I say it sometimes makes sense to build a little flow test like this. Because if you think that stop script, or return, or pause, or whatever you use does a certain thing and you put it in your code, and then it behaves differently, but you don’t recognize that immediately, then you think you have problems and errors at some other places.
So that’s, that’s why it’s sometimes good to build little test scripts like this where you just test the flow of what you try to do. If then next. If that condition you try to use, that and, or, next, while, do, whatever, if that actually behaves in the way you think it should behave. And then you fill it with your real code and the real condition.
So in that case let’s just run it again. You see we launched the two threads and we have a wait command and we have to stop script at the end, so what do you expect what happens? It runs, see the counters go up, now it executed stop script and it immediately stopped both threads. So another good thing here to test, I didn’t do that, but in the past I also recognized different behavior in that kind of stuff between Ubot studio and compiled bots. So it might also be a good idea to just have that quick test, compile it, and see if the behavior in a compiled bot is still the same. So I have to tell you that when it comes to multithreading, Ubot is not the smartest thing on the planet for this. Yeah, that’s all I want to say.
So there are a lot of scenarios and situations where stuff’s just doing something that’s a little bit weird. I still use… What’s the version I have here? 5.9.55. I just updated to the latest updated version, version six, because I wanted to show you that code there as well, but… Try, if you have version six, try to put a stat monitor into the code and then switch between code view and node view, and just see what happens or how long it takes. I have no idea what they’re doing, but switching between that, what you see here? Within just a couple of seconds. One, two, three. That takes like 20 or 30 seconds when you have a stat-mon in the code. I don’t know if it happens with other UI elements as well, but yeah, anyways, that’s why I did not show you the stuff with Ubot six because it just takes too long.
So now, stop script, you saw we cannot use. If you put stop script in the UI button, it’s just the same thing. You can then execute that button. Let’s run the counter. It’s counting up. The threads are running. I hit stop. You see sometimes… It didn’t get it. Let’s do it again. That’s the kind of weird stuff I’m talking about. So not doing anything… Interesting. Now let’s execute it here, when you right click it. No you can’t. So that’s really strange. Because if you’re execute it in the UI button then it’s not stopping the stuff. Not quite sure. I think I tested it at one point and it was actually stopping it.
But anyways, why is there different behavior. That’s nothing that’s documented anywhere and it’s hard to explain. The UI button probably executes it in a different thread, and we can run another test. Like what if you wait five seconds, and then we execute that stop script in a thread. So you can do all those weird things just to test how that stuff works and what the actual flow is. Well let’s see what happens. You see? Now it stops it again, but when you hit the UI button then it does not. So I don’t know. Sometimes the behavior is not really logical when you’re a normal developer that is used to object oriented programming with visual studio and C# and other languages. There are things behaving different with Ubot sometimes. So let’s see, what else can we do? We couldn’t put a stop script into that other thread, but that’s doing basically exactly the same thing. After five cycles it executes the stop script and it stops the whole thing.
So now how can you control this now. I mean you can always have a condition. If-then-else and you have an external variable or a global variable called “stop thread” for example, that you check. Like if stop thread equals true then whatever. But the question is what the script should do then. Because you cannot execute stop script. And there is no command for stop thread when you look at that. There’s thread spawn but that’s a different thing. We’re not talking about this today. So what do you do? Stop script you can’t use.
So another thing that you might maybe think about is a return. So what happens, and this is now another strange behavior, what happens when we put a return in between that code in the thread? And I will show you a proper way of doing that in a couple of minutes. I don’t try to talk too much today. So 14 minutes in, we will hurry up a little bit. So add in a return here. Execute that. Now see guys, what’s happening? What do you expect? Bam. It’s stopping the whole thing. You execute a return, returning out of a thread. In all other languages when you break out of thread code, then you will return back to the main code, but stuff that is executed in a different thread, why should it be influenced by a return from within another thread? That makes absolutely no sense at all.
Return in Ubot studio is a command that is in conjunction or used together with defines. So returning out of a define, that is what this is supposed to do. From a logical point of view an amount will return. It should also somehow work with threads, but that’s not how this is designed. So if you’re a developer and you’re coding other stuff, keep that in mind that the normal logic that you maybe have in your head, how stuff normally works, sometimes does not work in Ubot Studio. And before you pull out your hair, write a little test code and then see how that works.
Okay, let’s take a look how to do this properly. So this is exactly the same code, the only difference is instead of launching the threads directly, I put them into defined commands. We have defined thread-starter one, which has the exact same code as before, like loop-20. We have defined thread-starter two. That we remove for now. We have defined thread-starter two with the two threads, one and so on. So now we execute that. We have to recall the threads here. And it’s running. So all good. What happens if you press that stop button? Nothing happens. What happens when we put a wait here and then we execute stop script. It stops the whole script and stops all the threads.
Now what happens if we add the stop script into that define? Into the thread in the define, let’s run that. It stops the whole thing as well. So now the difference now is you can now break out of the define with a return. So if we put a return into that thread code. Thread-starter two defined, and we have the thread command, and we have that first loop, the return, and the second loop. So if everything is correct the second counter should stop at six. And the other one should still run. Let’s do that. See what happens. You see, the second one stopped at 6. The other one is still running. And that’s the reason why you always have to use define commands when you want to use threads, and exactly in that structure. You have the define, you have your thread within the define, and then you have your code, and you call that define to launch the code. That’s the way you do this. You don’t have the define within the thread. You don’t put the launch to the call of the define into a thread or other weird stuff.
We got many ways to do that. But they’re all showing different behavior, and sometimes not even working at all. So the proper way of doing this, and I showed you this already in the first multithreading video. Today I want to show you how to stop the different things. So here’s the thing. If you launch multiple threads and you have your code in your define, you can always have a condition. Here, let’s say this… ExBrowser element exists, whatever. Then you continue. Or else if it does not exist, then you break out of the code. With those conditions, you can always check in between your code if the stuff is there and then you just leave the thread.
Of course you should also have some error handling. Error management. Add to log file. So that you know when that condition executes and where it executes. What’s the thread? What’s the current status? At what point does it bail? What’s the website that this thread is currently processing? Mark that website in your table that it’s not finished and so on, so you can later look at it or repeat it and so on. That’s state management. If you just check it, it’s not there, and then you bail… Yeah, well then your code is not running into errors and exceptions. That’s already good thing, but you also don’t know what is going on. You don’t have any logs. You can’t tell where it failed. You know nothing.
If you will run 500 threads and then 370 executed well and the rest did not. So now what? Which one did not work? You don’t know. Where did they fail? You have no idea. So this is really, really important. That’s the way how to stop those threads. Of course you can also check other stuff. You can also check a variable. Like you have a global thread stop variable. You run a comparison and you have your thread stop equals true for example. And then before you launch all the threads you set that variable to false. And then at some point via a button or whatever, or some other outside condition that’s managing your threads, you set that variable to true. And then all threads who reach that point, they execute all-threads-stop equals true, so all threads will stop and maybe finish some stuff and so on.
Because then you can decide at what point different threads should stop. If you just execute a stop thread or stop script, you saw that it’s not really working when you push a button. So I don’t really know honestly what the condition is here, when stop script actually stops the script. Why it’s not doing that when you push a button, I don’t know. Maybe this is a bug or by design. I mean documentation is limited. But when you have that conditional check in your code you can use a global variable that you change from the outside, because that is working. You can change that variable via a button, and then the code will, within the normal flow of the thread code, it will come to that conditional check and then see, okay, thread-stop equals true. Now I have to stop.
And then you have, of course not like this, but then you have that return, for example. And it breaks out of the define. Like this. That’s it for today. Just wanted to give you some ideas how to control the flow of the threads and show you the behavior of stop script, return, and how to do that properly with the define. Yeah, that’s it for today. I hope this helped. If you have ideas for other tutorials or stuff, just post in the Facebook group or send me a message on Facebook. Email email@example.com or open a support ticket, whatever, and send me your ideas of what you think could be useful. Keep in mind it should be something that is helpful for other people. So not too specific to your project or your particular website. It should be some kind of generic thing that people could face on other pages and other projects as well. So thanks for listening. Wish you an amazing day. Talk to you soon. Bye-bye. Ciao.