Multithreading in Python
Tags: python, programming
Published: 2024-03-27 02:25:26

<p>Normally, instructions in a computer program are executed in a sequential way, the execution of one task has to be complete before the next one begins. In some cases, however, we may want some tasks to be executed simultaneously&nbsp;or in overlapping periods of time,&nbsp;&nbsp;multi-threading is one way that we can achieve this quickly and efficiently. In computer programming terms, handling multiple tasks at the same time is referred&nbsp;to as <a href="https://www.pynerds.com/introduction-to-concurrent-programming-in-python/">concurrency</a>, and therefore, multi-threading is&nbsp; just one way that a program can achieve concurrency.</p>

<p>Before we dive deeper&nbsp;onto how threads and multithreading works, you first need to understand two terms i.e <strong>process</strong> and <strong>thread</strong>.&nbsp;</p>

<p>In Python, a process refers to an instance of a program which is being executed. The operating system can handle multiple processes at the same time while ensuring data-integrity so&nbsp;that one process cannot access another process&#39;s data. Each process is allocated its own memory space where it stores and manipulates its data.</p>

<p>A thread, also known as a sub-process, refers to the smallest set of instructions that can&nbsp;be scheduled to be run by the processor.&nbsp;Threads are lightweight compared to processes,&nbsp; a single process can contain multiple threads.&nbsp;Each process&nbsp;always starts with just one thread which is referred to as the <strong>main thread,</strong>&nbsp;new threads can then be added to handle sub-tasks. All threads running in a single process shares the same resources/memory this makes&nbsp;communication between them easier and straightforward.</p>

<p>Multithreading&nbsp;happens when two or more&nbsp;threads appearing in a single process&nbsp;gets executed in overlapping periods of time. However, one thing that should be clear is that only a single thread can actually be executed at any given time in a process. Thus, in reality,&nbsp; multi-threading is not entirely simultaneous, instead, concurrency is achieved by switching very quickly between the threads, the switching is so fast that one might be forgiven for thinking that the threads are been executed in parallel.</p>

<p>The main purpose of multithreading is to improve program efficiency and performance as it makes&nbsp;it possible to execute multiple tasks simultaneously.&nbsp;</p>

<h2>The threading module</h2>

<p>In Python, multithreading is implemented using&nbsp;the <code>threading</code>&nbsp;module which is available in&nbsp;the standard library. The module offers the necessary tools for managing and working with threads.</p>

<p>The <code>Thread </code>class in the module, is used create, run and generally manage threads. Its syntax is as follows:</p>

<div class="syntax">
<pre>
<code class="language-python">threading.Thread(target = None, name = None, args = None, kwargs = None, deamon = None)</code></pre>
</div>

<div class="parameters">
<table border="1" cellpadding="1" cellspacing="1" class="table table-bordered table-light" style="width:100%">
	<tbody>
		<tr>
			<td><code>target</code></td>
			<td>A function that will be called when the thread starts. It defaults to <code>None</code>, if no function is to given.</td>
		</tr>
		<tr>
			<td><span style="font-family:monospace">name</span></td>
			<td>The thread name. If no name is given, a unique one is auto-generated such as <code>Thread-1</code>. Multiple threads can have&nbsp;a common name.</td>
		</tr>
		<tr>
			<td><span style="font-family:monospace">daemon</span></td>
			<td>A boolean value indicating whether the thread should run in the background. It defaults to <code>False </code>meaning that the thread does not run in the background.&nbsp;</td>
		</tr>
		<tr>
			<td><code>args and kwargs</code></td>
			<td>Optional argument that will be passed to the target function when the thread starts.</td>
		</tr>
	</tbody>
</table>
</div>

<p>Consider the following example.</p>

<pre>
<code class="language-python">import threading
import time

def square(num):
    for i in range(num):
        print(f"square({i}) = ", i ** 2)
        time.sleep(0.1)

def cube(num):
    for i in range(num):
        print(f"cube({i}) = ", i ** 3, end = '\n\n')
        time.sleep(0.1)


if __name__ == "__main__":
    # Create two thread for each function/task
    thread1 = threading.Thread(target=square, args = (6,))
    thread2 = threading.Thread(target=cube, args = (6,))

    #Start the threads
    thread1.start()
    thread2.start()

    # The main thread waits for both threads to finish
    thread1.join()
    thread2.join()

</code></pre>

<div class="output"><p>Output: </p>
<p>square(0) = &nbsp;0<br />
cube(0) = &nbsp;0</p>

<p>square(1) = &nbsp;1<br />
cube(1) = &nbsp;1</p>

<p>square(2) = &nbsp;4<br />
cube(2) = &nbsp;8</p>

<p>square(3) = &nbsp;9<br />
cube(3) = &nbsp;27</p>

<p>square(4) = &nbsp;16<br />
cube(4) = &nbsp;64</p>

<p>square(5) = &nbsp;25<br />
cube(5) = &nbsp;125</p>
</div>

<p>Normally, we would have expected the first function i.e <code>square </code>to be run until complete before the other function(<code>cube</code>)&nbsp; is started. But&nbsp; as you can see in the above example, the two functions,&nbsp; <code>square </code>and <code>cube</code>, are been run concurrently</p>

<p>As shown from the previous example, implementing multithreading&nbsp;involves the following basic steps:</p>

<div>
<ol>
	<li>
	<h4>Creating the thread&nbsp;</h4>

	<p>This involves creating an instance of the<code> threading.Thread() </code>class. We pass the necessary arguments such as the target function, name of the thread, arguments to the target function if necessary, etc.&nbsp;</p>

	<pre>
<code class="language-python">thread1 = threading.Thread(target=square, args = (6,))
thread2 = threading.Thread(target=cube, args = (6,))</code></pre>
	</li>
	<li>
	<h4>Starting the thread</h4>

	<p>To start the execution of the thread we call the&nbsp;<code>start()</code> method of thread objects .</p>

	<pre>
<code class="language-python">thread1.start()
thread2.start()</code></pre>
	</li>
	<li>
	<h4>Wait for the thread</h4>

	<p>When the thread starts, the main thread keeps on running as well. Calling the<code> join()</code> method of the new thread makes the main thread(or the calling thread to be precise) to pause and wait until the execution of the new thread is complete.</p>

	<pre>
<code class="language-python"> thread1.join()
 thread2.join()</code></pre>
	</li>
</ol>
</div>

<div>
<ol>
</ol>
</div>

<h2>Daemon threads</h2>

<p>Daemon threads are threads that runs indefinitely&nbsp;until the program quits. They are mostly suitable for handling regular and repeating tasks that needs to run in the background.</p>

<p>To create a daemon thread, we can set the <code>daemon </code>parameter to <code>True</code> when instantiating a <code>Thread </code>object or we can call the <code>setDaemon() </code>function after we have already instantiated the&nbsp;&nbsp;thread.</p>

<pre>
<code class="language-python">#import the Thread class
from threading import Thread

import time


def task():
    for i in range(20):
         print(i)
         time.sleep(0.1)

#a background task that runs at regular intervals
def background_task(main_task):
     while main_task.is_alive():
        print("daemon task")
        time.sleep(0.5)

if __name__ == "__main__":
     #regular thread
     T1 = Thread(target = task)
     #daemon thread
     T2 = Thread(target = background_task, kwargs = {"main_task": T1}, daemon = True)

     #start the threads
     T1.start()
     T2.start()
   
     T1.join()
     T2.join()</code></pre>

<div class="output"><p>Output: </p>
<p>daemon task<br />
0<br />
1<br />
2<br />
3<br />
4<br />
daemon task<br />
5<br />
6<br />
7<br />
8<br />
9<br />
daemon task<br />
10<br />
11<br />
12<br />
13<br />
14<br />
daemon task<br />
15<br />
16<br />
17<br />
18<br />
19<br />
daemon task</p>
</div>

<p>Thread objects also contains the<code> isDaemon() </code>method which can be used to check whether a thread is a Daemon thread.</p>

<pre>
<code class="language-python">import threading

t1 = threading.Thread(daemon = True)
t2 = threading.Thread()

#check whether thread is a daemon
print(t1.daemon)
print(t2.daemon)</code></pre>

<div class="output"><p>Output: </p>
<p>True<br />
False&nbsp;</p>
</div>

<h2>Thread states&nbsp;</h2>

<p>Threads can be in any of the following states:</p>

<div>
<ul>
	<li>New - The thread has only been instantiated but has not been started, no resources has yet been allocated for it.</li>
	<li>Runnable&nbsp;- In this state, the thread is waiting to run, it has all the necessary resources, only that the scheduler has not yet scheduled it to run.</li>
	<li>Live - In this state, the thread is executing its&nbsp;task .</li>
	<li>Paused - The thread has been paused in some way. For example, when&nbsp;it is waiting for another thread to complete&nbsp;&nbsp;the execution.</li>
	<li>Dead - The&nbsp; thread is not runnable.&nbsp; A thread can can reach this state naturally when its task is complete or if it has been&nbsp;&nbsp;killed unnaturally</li>
</ul>
</div>

<p>The Thread class includes the <code>is_live()</code> method which can be used to tell whether a thread is currently running.</p>
<div class="related"><h3>Related articles</h3><hr><ul><li><p><a class="text-success" href="https://www.pynerds.com/introduction-to-programming-languages/">Introduction to Programming Languages</a><li><a class="text-success" href="https://www.pynerds.com/introduction-to-object-oriented-programming/">Introduction to Object Oriented Programming</a><li><p><a class="text-success" href="https://www.pynerds.com/introduction-to-concurrent-programming-in-python/">Introduction to concurrent programming in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/multiprocessing-in-python/">Multiprocessing in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/asynchronous-programming-with-python/">Asynchronous Programming with Python</a><li><p><a class="text-success" href="https://www.pynerds.com/understand-gil-in-python-the-global-interpreter-lock/">Understand GIL in Python - The Global Interpreter Lock</a><li><p><a class="text-success" href="https://www.pynerds.com/subprocesses-in-python/">Subprocesses in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/asyncawait-in-python/">async/await in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/how-to-use-asyncio-in-python/">How to use asyncio in Python</a></ul></div>