< Python Panorama Series-5 > Unlocking Python Concurrent Programming: The Mystery of Multi-threading and Multi-Processing Revealed

Welcome to our blog series "Python Panorama Series"! In this series, we'll take you from the basics of Python to advanced topics, helping you master this powerful and flexible programming syntax. Whether you are new to programming or a developer with a certain foundation, this series will provide the knowledge and skills you need.

In this fifth installment of the series, we'll dive into concurrent programming in Python, with a special focus on multithreading and multiprocessing applications. We'll start with basic concepts, then explore each mechanism with detailed examples, and finally share some hands-on experience and an elegant programming technique.

Part 1: Introduction to Multithreading

A thread is the smallest unit of execution in an operating system. Within a single program or process, multiple threads can run concurrently, sharing process resources such as memory and file descriptors.

1.1 Multithreading in Python

Python supports multi-threaded programming and provides `threading` module as support. This module provides the `Thread` class that we can create new threads by creating an instance of it and passing a function to it. Of course, you can also create custom threads by inheriting the `Thread` class and overriding the `run()` method. The following is an example of multithreaded programming:

import threading

def print_numbers():
    for i in range(10):
        print(i)

def print_letters():
    for letter in 'abcdefghij':
        print(letter)

# create thread
t1 = threading.Thread(target=print_numbers)
t2 = threading.Thread(target=print_letters)

# start thread
t1.start()
t2.start()

# wait for the thread to finish
t1.join()
t2.join()

In the above example, we defined two functions: one to print numbers and the other to print letters. Then we create two threads, each thread's goal is to execute these functions. The `start()` method is used to start the thread, and the `join()` method is used to wait for the thread to complete.

1.2 Practical application of multithreading

Although Python's multi-threading cannot achieve true parallelism due to the existence of the Global Interpreter Lock (GIL), they are still useful in I/O-intensive tasks. The GIL is a mutex for the CPython interpreter, ensuring that only one thread is executing at any time. This means that in CPU-intensive tasks, multi-threading may not be the best choice, because they cannot fully utilize multi-core CPUs.

However, in I/O intensive tasks, multithreading can improve program performance. For example, if a program needs to download files from multiple sources, using multiple threads can allow other threads to continue downloading other files while one thread waits for a response from the network. In this way, the program can download files from multiple sources at the same time, greatly improving efficiency.

Part II: Introduction to Multiprocessing

A process is an independent execution entity in the operating system, and each process has its own resources such as memory space and file descriptors. Unlike threads, resources between processes

Not shared, each process has its own independent resources.

2.1 Multiprocessing in Python

Python provides multiprocessing support through the `multiprocessing` module. Similar to multithreading, we can create new processes by creating an instance of the `Process` class and passing it a function. We can also create custom processes by extending the `Process` class and overriding the `run()` method.

The following is an example of simple multi-process programming:

import multiprocessing

def print_numbers():
    for i in range(10):
        print(i)

def print_letters():
    for letter in 'abcdefghij':
        print(letter)

# create process
p1 = multiprocessing.Process(target=print_numbers)
p2 = multiprocessing.Process(target=print_letters)

# start the process
p1.start()
p2.start()

# wait for the process to finish
p1.join()
p2.join()

This example is similar to the previous multi-threaded example, the difference is that here we create two processes instead of threads.

2.2 Practical application of multi-process

Multi-processing can achieve true parallelism, allowing Python programs to take advantage of multi-core CPUs. Therefore, for CPU-intensive tasks, multiprocessing often has advantages over multithreading. On the other hand, the overhead of multi-processing is greater than that of multi-threading, and the communication and synchronization between processes is more complicated than that between threads. Therefore, for I/O-intensive tasks, or tasks that require frequent communication, multithreading may be a better choice.

Part III: Tips for Optimizing Concurrent Programming

In Python, the `concurrent.futures` module provides a high-level interface for multi-threaded and multi-process programming, allowing us to write code more concisely.

This module provides `ThreadPoolExecutor` and `ProcessPoolExecutor` two classes, which are used to create thread pool and process pool respectively. Both classes implement the same interface, you can use the `submit()` method to submit the task, and then use the `as_completed()` function to wait for the task to complete.

Here is an example using the `concurrent.futures` module:

import concurrent.futures

def print_numbers():
    for i in range(10):
        print(i)

def print_letters():
    for letter in 'abcdefghij':
        print(letter)

# use thread pool
with concurrent.futures.ThreadPoolExecutor() as executor:
    future1 = executor.submit(print_numbers)
    future2 = executor.submit(print_letters)
    for future in concurrent.futures.as_completed([future1, future2]):
        pass

# use process pool
with concurrent.futures.ProcessPoolExecutor() as executor:
    future1 = executor.submit(print_numbers)
    future2 = executor.submit(print_letters)
    for future in concurrent.futures.as_completed([future1, future2]):
        pass

In the above example, we created thread pool and process pool, and then submitted tasks to them. As you can see, using the `concurrent.futures` module, our code is more concise, and the readability and maintainability are also improved.

Summarize

Python's multithreading and multiprocessing are very powerful tools that can help us write more efficient programs. However, they also have their own advantages and disadvantages, and we need to choose according to specific tasks and needs. At the same time, Python also provides the `concurrent.futures` module, which can make our concurrent programming easier and more efficient.

We hope this article helps you better understand and use Python's multithreading and multiprocessing. If you have any questions or suggestions, please leave a message in the comment area.

[Get the update information of Python full perspective at the first time, please pay attention to my WeChat official account:  Python full perspective ]

Guess you like

Origin blog.csdn.net/magicyangjay111/article/details/130788691