Do you know when to use multithreading? What are the advantages and disadvantages?
This episode continues what will likely be several more episodes devoted to multithreading. Listen to this episode, or read the full transcript below, to learn about the following four reasons to use multithreading:
- When you’re about to call some method that could take a long time to complete and you don’t want to wait around.
- When you want to setup some completely independent part of your application.
- When you have a problem that lends itself to being divided up into smaller problems that can be recombined later to form the final answer.
- When you want to perform the same task multiple times in slightly different ways.
When your application starts, it’s running as a process so that the operating system can isolate everything that happens in your process from all the other processes that are running. A process doesn’t really run though, at least not on modern operating systems. Older versions of Unix used processes exclusively and had no concept of threads.
The idea of threads was to allow for mini-processes. You see, it’s expensive to start a process because of all the isolation and security that the operating system needs to setup and maintain. So whenever you want to do multiple things, instead of starting a new instance of your entire process, you just create a thread that runs inside your process.
I use the words process and application a lot to mean the same thing. the only real difference is that users normally think of applications as the programs that they run. And now that I say it, I also use program too. All these really mean about the same thing. However, an operating system usually has many processes running that the user is normally unaware of. An application is just a process that the user knows about and usually has a main window. And a program is the same as an application. I can’t think of any meaningful difference between an application and a program.
For thread aware operating systems, which should include almost all of them these days, when your application starts, the operating system will create your first thread for you. This is sometimes called the main thread. If your application doesn’t use multithreading, then you may not even be aware that the whole system enabling your code to run is based on a thread provided for you.
Your main thread and any other threads you create all run inside your process. They share the same memory space and default user identity and security.
Other than your main thread, if you want another thread, you need to create it yourself. You do this by calling a method usually provided by your operating system or maybe the library that comes with your language also has this ability. If your library allows you to create additional threads, it’ll eventually need to call an operating system method anyway. the nice thing about using a library method is that it makes your code more portable to other platforms.
Alright, so when should you create a new thread? There are many reasons. Here are probably the four most common that I can think of:
◦ #1 When you’re about to call some method that could take a long time to complete and you don’t want to wait around.
◦ #2 When you want to setup some completely independent part of your application.
◦ #3 When you have a problem that lends itself to being divided up into smaller problems that can be recombined later to form the final answer.
◦ #4 When you want to perform the same task multiple times in slightly different ways.
I’ll explain more details about each of these four reasons right after this message from our sponsor.
(Message from Sponsor )
The first reason is by far the most common reason that I’ve had for multithreading. Let’s say you have a graphical application. I don’t mean an application to create graphic images. I mean an application that has windows that can be moved around on the screen, with menus, buttons, places to click and type text, that type of graphical application. This is usually called a GUI which stands for graphical user interface.
Let’s say that this application allows users to download files and do something with them. What it does is not important right now. The fact that it allows a file to be downloaded from the internet means that the app will have to wait for the file to be received. This could be quick or something users should make sure to start before going out for lunch. You really can’t tell. The moment the user clicks a button to begin the download, if you don’t perform the actual file download on a different thread, then your main thread will be stuck waiting for the file to complete.
This is bad because when your main thread is stuck, your whole application stops working. The user can probably still move the main window around on the screen but that’s only because the operating system is doing that work. Usually, your application will turn into a mess of leftover images that show through your main window. This is because your main thread is busy waiting for the file to complete and can’t also be redrawing the main window.
The second reason allows you to simplify your code if you have aspects of your application that are very different and need to run independently. Maybe you want your application to check for new versions of your application. This is a good idea to keep your customer on the latest version of your software. Checking for those updates has nothing to do with the normal operation of your program and trying to do all that in one thread just makes things more complicated. So split them into different threads and let them each do their own thing.
The third reason will allow you to take advantage of multiple cores. So far, yes, the threads could be run on different cores, but that wasn’t the main motivating factor. This time it is. If you need to calculate results for maybe one hundred separate events, then consider splitting this into four groups of 25. Hand each group off to a new thread that’s dedicated to solving just its own group. If you’ve got multiple cores and set this up properly, then you should finish calculated all 100 events in the time it takes to calculate only 25. That’s because each thread running in its own core is able to calculate its own 25 events at the same time.
And the fourth reason also helps you to simplify your code but not for drastically different tasks. This time for similar tasks that can be run independently of each other. This reason is kind of like a combination of the second and third reasons.
These have all been some good reasons for multithreading. But what are some of the disadvantages?
First of all, when you start a new thread, you probably need to know when it completes. And once it does complete, you need to get the result. There’s a lot more code involved to do this than just calling a method and directly waiting on the method to return with the result.
You need to be aware of synchronization issues, deadlocks, race conditions, debugging, and a whole lot more. Multithreading is not something that you should just add to your application and hope you get it right. I can guarantee that if you don’t fully understand how to write proper multithreaded code, that you’ll definitely get it wrong. There’s just too many ways to make mistakes.
This will need a lot more episodes. I don’t know yet how many. Probably not as many as all the design pattern episodes. But we’ll see.