Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Concurrency dalam Python dengan Threading dan Multiprocessing

Concurrency dalam Python dengan Threading dan Multiprocessing

DDD
DDDasal
2024-09-14 06:23:12801semak imbas

Concurrency in Python with Threading and Multiprocessing

Concurrency ialah idea penting dalam pengaturcaraan moden yang membenarkan berbilang tugasan dijalankan pada masa yang sama untuk meningkatkan prestasi aplikasi.

Terdapat beberapa cara untuk mencapai konkurensi dalam Python, dengan penjalinan dan pemprosesan berbilang adalah yang paling terkenal.

Dalam artikel ini, kami akan meneroka kedua-dua kaedah ini secara terperinci, memahami cara ia berfungsi dan membincangkan masa untuk menggunakan setiap kaedah, bersama-sama dengan contoh kod praktikal.


Apakah itu Concurrency?

Sebelum kita bercakap tentang threading dan multiprocessing, adalah penting untuk memahami maksud concurrency.

Concurrency ialah apabila program boleh melakukan berbilang tugas atau proses pada masa yang sama.

Ini boleh menjadikan program menggunakan sumber lebih baik dan berjalan lebih pantas, terutamanya apabila ia perlu melakukan perkara seperti membaca fail atau melakukan banyak pengiraan.

Terdapat dua cara utama untuk mencapai keselarasan:

  • Paralelisme: Menjalankan berbilang tugas pada masa yang sama pada bahagian pemproses komputer yang berlainan.
  • Concurrency: Mengendalikan berbilang tugas dalam tempoh masa yang sama, tetapi tidak semestinya pada saat yang sama.

Python menawarkan dua cara utama untuk mencapai keselarasan:

  • Threading: Untuk tugasan yang boleh diuruskan pada masa yang sama.
  • Pemprosesan berbilang: Untuk tugasan yang perlu dijalankan secara serentak pada teras pemproses yang berbeza.

Membentang dalam Python

Threading membolehkan anda menjalankan berbilang unit proses yang lebih kecil, dipanggil benang, dalam proses yang sama, berkongsi ruang memori yang sama.

Benang lebih ringan daripada proses dan bertukar antara mereka lebih cepat.

Walau bagaimanapun, threading dalam Python tertakluk kepada Global Interpreter Lock (GIL), yang memastikan hanya satu thread boleh melaksanakan kod Python pada satu masa.

Bagaimana Threading Berfungsi

Modul benang Python menyediakan cara yang mudah dan fleksibel untuk mencipta dan mengurus benang.

Mari kita mulakan dengan contoh asas:

import threading
import time


def print_numbers():
    for i in range(5):
        print(f"Number: {i}")
        time.sleep(1)


# Creating a thread
thread = threading.Thread(target=print_numbers)

# Starting the thread
thread.start()

# Wait for the thread to complete
thread.join()

print("Thread has finished executing")


# Output:
# Number: 0
# Number: 1
# Number: 2
# Number: 3
# Number: 4
# Thread has finished executing

Dalam contoh ini:

  • Kami mentakrifkan fungsi print_numbers() yang mencetak nombor dari 0 hingga 4 dengan kelewatan satu saat antara cetakan.
  • Kami mencipta benang menggunakan threading.Thread() dan lulus print_numbers() sebagai fungsi sasaran.
  • Kaedah start() memulakan pelaksanaan utas dan join() memastikan program utama menunggu urutan selesai sebelum meneruskan.

Contoh: Threading untuk Tugasan Terikat I/O

Threading amat berguna untuk tugas terikat I/O, seperti operasi fail, permintaan rangkaian atau pertanyaan pangkalan data, di mana program menghabiskan sebahagian besar masanya menunggu sumber luaran.

Berikut ialah contoh yang mensimulasikan muat turun fail menggunakan benang:

import threading
import time


def download_file(file_name):
    print(f"Starting download of {file_name}...")
    time.sleep(2)  # Simulate download time
    print(f"Finished downloading {file_name}")


files = ["file1.zip", "file2.zip", "file3.zip"]

threads = []

# Create and start threads
for file in files:
    thread = threading.Thread(target=download_file, args=(file,))
    thread.start()
    threads.append(thread)

# Ensure all threads have finished
for thread in threads:
    thread.join()

print("All files have been downloaded.")

# Output:
# Starting download of file1.zip...
# Starting download of file2.zip...
# Starting download of file3.zip...
# Finished downloading file1.zip
# Finished downloading file2.zip
# Finished downloading file3.zip
# All files have been downloaded.

Dengan mencipta dan mengurus urutan berasingan untuk setiap muat turun fail, program ini boleh mengendalikan berbilang tugas serentak, meningkatkan kecekapan keseluruhan.

Langkah utama dalam kod adalah seperti berikut:

  • Fungsi download_file ditakrifkan untuk mensimulasikan proses muat turun.
  • Senarai nama fail dibuat untuk mewakili fail yang perlu dimuat turun.
  • Untuk setiap fail dalam senarai, urutan baharu dibuat dengan download_file sebagai fungsi sasarannya. Setiap urutan dimulakan serta-merta selepas penciptaan dan ditambahkan pada senarai urutan.
  • Atur cara utama menunggu semua urutan selesai menggunakan kaedah join() , memastikan program tidak diteruskan sehingga semua muat turun selesai.

Had Threading

Walaupun threading boleh meningkatkan prestasi untuk tugas terikat I/O, ia mempunyai had:

  • Global Interpreter Lock (GIL): GIL mengehadkan pelaksanaan kepada satu thread pada satu masa untuk tugasan terikat CPU, mengehadkan keberkesanan threading dalam pemproses berbilang teras.
  • Syarat Perlumbaan: Memandangkan utas berkongsi ruang memori yang sama, penyegerakan yang tidak betul boleh membawa kepada keadaan perlumbaan, di mana hasil sesuatu program bergantung pada pemasaan utas.
  • Kebuntuan: Benang yang menunggu antara satu sama lain untuk melepaskan sumber boleh menyebabkan kebuntuan, apabila tiada kemajuan dibuat.

Pemprosesan berbilang dalam Python

Pemprosesan berbilang menangani had penjalinan dengan menggunakan proses yang berasingan dan bukannya utas.

Setiap proses mempunyai ruang memori sendiri dan penterjemah Python, membenarkan keselarian sebenar pada sistem berbilang teras.

Ini menjadikan pemproses berbilang sesuai untuk tugasan yang memerlukan pengiraan berat.

Bagaimana Multiprocessing Berfungsi

Modul berbilang pemprosesan dalam Python membolehkan anda mencipta dan mengurus proses dengan mudah.

Let’s start with a basic example:

import multiprocessing
import time


def print_numbers():
    for i in range(5):
        print(f"Number: {i}")
        time.sleep(1)


if __name__ == "__main__":
    # Creating a process
    process = multiprocessing.Process(target=print_numbers)

    # Starting the process
    process.start()

    # Wait for the process to complete
    process.join()

    print("Process has finished executing")

# Output:
# Number: 0
# Number: 1
# Number: 2
# Number: 3
# Number: 4
# Process has finished executing

This example is similar to the threading example, but with processes.

Notice that the process creation and management are similar to threading, but because processes run in separate memory spaces, they are truly concurrent and can run on different CPU cores.

Example: Multiprocessing for CPU-Bound Tasks

Multiprocessing is particularly beneficial for tasks that are CPU-bound, such as numerical computations or data processing.

Here’s an example that calculates the square of numbers using multiple processes:

import multiprocessing


def compute_square(number):
    return number * number


if __name__ == "__main__":
    numbers = [1, 2, 3, 4, 5]

    # Create a pool of processes
    with multiprocessing.Pool() as pool:
        # Map function to numbers using multiple processes
        results = pool.map(compute_square, numbers)

    print("Squares:", results)

# Output:
# Squares: [1, 4, 9, 16, 25]

Here are the key steps in the code:

  • A function compute_square is defined to take a number as input and return its square.
  • The code within the if name == "main": block ensures that it runs only when the script is executed directly.
  • A list of numbers is defined, which will be squared.
  • A pool of worker processes is created using multiprocessing.Pool().
  • The map method is used to apply the compute_square function to each number in the list, distributing the workload across multiple processes.

Inter-Process Communication (IPC)

Since each process has its own memory space, sharing data between processes requires inter-process communication (IPC) mechanisms.

The multiprocessing module provides several tools for IPC, such as Queue, Pipe, and Value.

Here’s an example using Queue to share data between processes:

import multiprocessing


def worker(queue):
    # Retrieve and process data from the queue
    while not queue.empty():
        item = queue.get()
        print(f"Processing {item}")


if __name__ == "__main__":
    queue = multiprocessing.Queue()

    # Add items to the queue
    for i in range(10):
        queue.put(i)

    # Create a pool of processes to process the queue
    processes = []
    for _ in range(4):
        process = multiprocessing.Process(target=worker, args=(queue,))
        processes.append(process)
        process.start()

    # Wait for all processes to complete
    for process in processes:
        process.join()

    print("All processes have finished.")


# Output:
# Processing 0
# Processing 1
# Processing 2
# Processing 3
# Processing 4
# Processing 5
# Processing 6
# Processing 7
# Processing 8
# Processing 9
# All processes have finished.

In this example:

  • def worker(queue): Defines a function worker that takes a queue as an argument. The function retrieves and processes items from the queue until it is empty.
  • if name == "main":: Ensures that the following code runs only if the script is executed directly, not if it is imported as a module.
  • queue = multiprocessing.Queue(): Creates a queue object for inter-process communication.
  • for i in range(10): queue.put(i): Adds items (numbers 0 through 9) to the queue.
  • processes = []: Initializes an empty list to store process objects.
  • The for loop for _ in range(4): Creates four worker processes.
  • process = multiprocessing.Process(target=worker, args=(queue,)): Creates a new process with worker as the target function and passes the queue as an argument.
  • processes.append(process): Adds the process object to the processes list.
  • process.start(): Starts the process.
  • The for loop for process in processes: Waits for each process to complete using the join() method.

Challenges of Multiprocessing

While multiprocessing provides true parallelism, it comes with its own set of challenges:

  • Higher Overhead: Creating and managing processes is more resource-intensive than threads due to separate memory spaces.
  • Complexity: Communication and synchronization between processes are more complex than threading, requiring IPC mechanisms.
  • Memory Usage: Each process has its own memory space, leading to higher memory usage compared to threading.

When to Use Threading vs. Multiprocessing

Choosing between threading and multiprocessing depends on the type of task you're dealing with:

Use Threading:

  • For tasks that involve a lot of waiting, such as network operations or reading/writing files (I/O-bound tasks).
  • When you need to share memory between tasks and can manage potential issues like race conditions.
  • For lightweight concurrency without the extra overhead of creating multiple processes.

Use Multiprocessing:

  • For tasks that require heavy computations or data processing (CPU-bound tasks) and can benefit from running on multiple CPU cores at the same time.
  • When you need true parallelism and the Global Interpreter Lock (GIL) in threading becomes a limitation.
  • For tasks that can run independently and don’t require frequent communication or shared memory.

Conclusion

Concurrency in Python is a powerful way to make your applications run faster.

Threading is great for tasks that involve a lot of waiting, like network operations or reading/writing files, but it's not as effective for tasks that require heavy computations because of something called the Global Interpreter Lock (GIL).

On the other hand, multiprocessing allows for true parallelism, making it perfect for CPU-intensive tasks, although it comes with higher overhead and complexity.

Que vous traitiez des données, traitiez plusieurs requêtes réseau ou effectuiez des calculs complexes, les outils de threading et de multitraitement de Python vous offrent ce dont vous avez besoin pour rendre votre programme aussi efficace et rapide que possible.

Atas ialah kandungan terperinci Concurrency dalam Python dengan Threading dan Multiprocessing. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn