Once the first phase of learning a programming language, such as Python, has been overcome, all the necessary foundations for the development of any program have been acquired. But going on to develop more complex programs and contributing to different projects, often together with other developers, you will have to learn a lot of other notions, such as Threads and the concept of Threading.
Threading is a technique that allows us to divide our program into different parts and then execute them independently, if not often, even competing. This technique is based, precisely, on the threads.
Threading is an efficient technique, or less, depending on the technology on which it is used, and the good implementation rules that this technique requires. Often, if these two factors are not well taken into consideration, threading can lead to lower efficiency compared to a program that does not use it, and sometimes even to disastrous results with loss of data and anomalous and unwanted behaviors.
It is therefore important to understand both the fundamental concepts of this technique, and how it is implemented in the various programming languages (such as Python), and on the potential and possibilities of the computer on which we are running it.
What are the Threads
In simple words, a thread is a separate flow of execution. In our programs that will use threading, threads will enclose a part of our code that we want to run independently from the rest of the program. While in computer science, the term thread (thread of execution) stands for the smallest executable unit that can be scheduled in an operating system. So the same term is used, but with slightly different meanings.
Thus the threads are related both to the code structure (parts of code) and to the operating system and number of processors in the system. These two things often do not match, but rather work on different levels. The threads of programming languages are at a high level, while threads sent to processors work at a low level. How then high-level threads are implemented at low level by interpreters and compilers are a whole other story.
447/5000Another aspect that can often lead to confusion with threads is the concept of competition. Often when we talk about threads, we immediately think of the possibility of having a program that simultaneously executes two different parts of code. But in reality the various threads present in a program, and the same thing, at a lower level, the threads of an operating system are not executed simultaneously, even if they seem to do so.
This also applies if you work on systems with multiple processors. In fact, with Python, the different threads can be executed on different processors, but they will be executed only one at a time. To be able to obtain instead that several operations are performed all simultaneously, it is necessary to use a non-standard implementation of Python, or make use of the multiprocessing module. We will see this concept in detail in another article.
Insights on Threads
Once the concepts that could lead to confusion have been clarified, and had a first look at the topic of threads. Let’s go a bit more in detail by adding other concepts.
Let’s start with a program in general that begins its execution by becoming a process. Each process can itself be considered a thread (main thread)
If the threading technique is used in the code, one or more threads will be formed at a particular point in the execution, called fork. The threads created share both the memory and the status of the process. In other words they share both the code, the instructions and the values of the variables. So if a thread will change a global variable, this will change for all other threads. However, a thread also has local variables available that will be accessible only to the single thread and will disappear after its execution.
At this point the different threads take away from autonomous execution routes (whether they are actually actually run in parallel or not). Even in a single processor system, threading can occur. In these systems the multitasking technique is adopted, which simulates the independent and parallel execution of processes thanks to scheduling and timeslicing techniques.
Threads in Python
Python, like many other programming languages, implements threads
There are two modules that support the use of Python threads:
- Thread (deprecated)
The Thread module has been considered “deprecated” and therefore is no longer to be used when we want to develop new Python code, while we can find it in the old codes. The interpreter still accepts it to maintain compatibility with older versions.
The Threading module
The best way to learn how a module works and how to use it in Python is to start working with examples.
In the first example, we will implement a single thread that waits for a certain period of time (eg 10 seconds) before displaying a message on the console. During the execution we will run 5 threads simultaneously.
from threading import Thread
print("thread ", i, " started")
print("thread ", i, " ended")
for i in range(5):
t = Thread(target=test, args=(i,))
From the threading module we import Thread, an interface in which to encapsulate a function and which corresponds to a thread. We define the implementation code of the thread within a function we will call test(), and which we will assign to the interface via the target parameter. While args is used to pass parameters within the thread.
So in the execution of the program we will start 5 threads in succession through a for loop. After assigning a thread to the variable t, the start() method will start its execution.
Executing the code…
As can be seen from the results, the 5 threads start in sequence. In fact the for loop goes on, gradually opening all the threads, without waiting for the end of their execution (otherwise the threads would not make sense). Then, as you can see from the result, the order in which these terminate is however random. And it varies from execution to execution.
This very simple example, besides introducing the use of threads, highlights one of the most important aspects: the results can vary from execution to execution; or better: the order of execution varies every time we launch the program. Then if the results of the program depend on this order, these will also vary. It is therefore important for those developing the program to take this into account and avoid unwanted behavior.
In this first part we introduced the concept of Threads and we saw a basic example in Python. In the following part we will see how to use the Joins to have more control over the threads.