Day 24: Threading and Multiprocessing

Day 24: Threading and Multiprocessing

Introduction

In the realm of software development, concurrent execution refers to the ability of a program to perform multiple tasks simultaneously. This is achieved by breaking down tasks into smaller units of execution and running them concurrently, rather than sequentially.
Concurrency and parallelism are two concepts often discussed in the context of concurrent programming in Python.

  1. Concurrency:

    • Concurrency is the capability of a program to handle and execute multiple tasks simultaneously, potentially overlapping in time. It does not necessarily mean that tasks are executed simultaneously, but rather that the program manages multiple tasks in a way that gives the appearance of simultaneous execution. Concurrency is typically achieved using threading.
  2. Parallelism:

    • Parallelism is the concurrent execution of numerous tasks or processes, often across various processors. Unlike concurrency, where tasks may not genuinely occur simultaneously, parallelism guarantees that tasks are executed concurrently on distinct computational units. Parallelism can be achieved using multiprocessing.

Threading and multiprocessing:

  1. Threading: Threading is a technique used to achieve concurrent execution within a single process. It involves dividing a program into multiple threads of execution, each capable of running independently.

    Threads share the same memory space and resources within the process, allowing them to communicate and synchronize data easily.

  2. Multiprocessing: Multiprocessing is running the multiple processes simultaneously, each with its own memory space and resources. Unlike threading, multiprocessing enables true parallelism by utilizing multiple CPU cores or processors. Processes are independent of each other and communicate through inter-process communication mechanisms.

Threading Example:

import threading
import time

def num():
    for i in range(1, 11):
        print(i)
        time.sleep(1)  #time-consuming operation

#creating a thread to execute 
thread = threading.Thread(target=num)
thread.start()
thread.join()

print("Thread execution complete!")
  • First we import the threading module, which provides support for working with threads in Python. We also import the time module for time-related operations.

  • We define a function num() that prints numbers from 1 to 10 using a for loop. Inside the loop, we print the current number and then call time.sleep(1) to simulate a time-consuming operation. This sleep pauses the execution of the thread for 1 second, simulating some work being done.

  • We create a thread object thread using threading.Thread, passing the target parameter as the num function. This prepares the thread to execute the num function when started.

  • We start the execution of the thread by calling thread.start(). This initiates the execution of the num function in a separate thread, allowing it to run concurrently with the main program.

  • We use thread.join() to wait for the thread to finish its execution. This line pauses the main program's execution until the thread has completed executing the num function.

After the thread has finished executing, we print "Thread execution complete!" to indicate that the thread has finished its task.

In summary, this code demonstrates the use of threading in Python to execute a function (print_numbers) concurrently in a separate thread. The main program waits for the thread to finish its task before proceeding further.

Multiprocessing Example:

import multiprocessing
import time

def num():
    for i in range(1, 11):
        print(i)
        time.sleep(1)  

#creating a process 
process = multiprocessing.Process(target=num)
process.start()
process.join()

print("Process execution complete!")
  1. We import the multiprocessing module, which provides support for working with processes in Python.

  2. We define a function num() that prints numbers from 1 to 10 using a for loop.

    Just like above example , pauses the execution of process for 1 sec .

  3. We create a process object process using multiprocessing.Process, passing the target parameter as the num function. This prepares the process to execute the num function when started.

  4. We start the execution of the process by calling process.start(). This initiates the execution of the num function in a separate process, allowing it to run concurrently with the main program.

  5. Using process.join() to wait for the process to finish its execution. This line pauses the main program's execution until the process has completed executing the num function.

  6. After the process has finished executing, we print "Process execution complete!" to indicate that the process has finished its task.

Conclusion

In summary, threading allows multiple threads to run within a single process, while multiprocessing involves running multiple processes simultaneously. Multiprocessing is typically used for CPU-bound tasks that can benefit from parallel execution, such as numerical computations, data processing. Threading is commonly used for tasks that involve I/O operations or managing multiple tasks simultaneously without blocking the main program's execution.