Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Mencipta dan Mengurus Tugas dengan Asyncio

Mencipta dan Mengurus Tugas dengan Asyncio

王林
王林asal
2024-08-15 06:37:08479semak imbas

Creating and Managing Tasks with Asyncio

Asyncio membenarkan pembangun menulis atur cara tak segerak dalam Python tanpa perlu bersusah payah. Modul ini juga menyediakan banyak cara tugas tak segerak dan dengan pelbagai cara untuk melakukannya, ia boleh mengelirukan untuk digunakan.

Dalam artikel ini, kami akan membincangkan banyak cara anda boleh membuat dan mengurus tugasan dengan asyncio.

Apakah tugas asyncio?

Dalam asyncio, tugas ialah objek yang membungkus coroutine dan menjadualkannya untuk dijalankan dalam gelung acara. Ringkasnya, tugas ialah satu cara untuk menjalankan coroutine serentak dengan tugasan lain. Setelah tugasan dibuat, gelung acara menjalankannya, menjeda dan menyambung semula tugasan yang perlu untuk membolehkan tugasan lain dijalankan.

Kaedah untuk Mencipta dan Mengurus Tugasan Asyncio

Kini, kita boleh membincangkan kaedah untuk mencipta dan mengurus tugas. Pertama, untuk mencipta tugasan dalam Python menggunakan asyncio, anda menggunakan kaedah asyncio.create_task yang mengambil hujah berikut:

  • coro (diperlukan): Objek coroutine yang akan dijadualkan. Ini ialah fungsi yang anda mahu jalankan secara tak segerak.

  • nama (pilihan): Nama untuk tugasan yang boleh berguna untuk tujuan penyahpepijatan atau pengelogan. Anda boleh menetapkan rentetan pada parameter ini.

    • Anda juga boleh menetapkan atau mendapatkan nama itu kemudian menggunakan Task.set_name(name) dan Task.get_name().
  • konteks (pilihan): Diperkenalkan dalam Python 3.11, ini digunakan untuk menetapkan pembolehubah konteks untuk tugasan, mendayakan storan setempat tugasan. Ia serupa dengan storan setempat benang tetapi untuk tugas asyncio.

    • Argumen ini tidak biasa digunakan melainkan anda berhadapan dengan senario lanjutan yang memerlukan pengurusan konteks.

Berikut ialah contoh penggunaan asyncio.create_task:

import asyncio

# Define a coroutine
async def greet(name):
    await asyncio.sleep(1)  # Simulate an I/O-bound operation
    print(f"Hello, {name}!")

async def main():
    # Create tasks
    task1 = asyncio.create_task(greet("Alice"), name="GreetingAlice")
    task2 = asyncio.create_task(greet("Bob"), name="GreetingBob")

    # Check task names
    print(f"Task 1 name: {task1.get_name()}")
    print(f"Task 2 name: {task2.get_name()}")

    # Wait for both tasks to complete
    await task1
    await task2

# Run the main function
asyncio.run(main())

Apabila anda membuat tugasan, anda boleh melaksanakan banyak kaedah seperti:

  • .cancel(): untuk membatalkan tugas.

  • .add_done_callback(cb): untuk menambah fungsi panggil balik yang berjalan apabila tugasan selesai.

  • .done(): untuk menyemak sama ada tugasan telah selesai.

  • .result(): untuk mendapatkan semula hasil tugasan selepas tugasan itu selesai.

Sekarang kita faham cara membuat tugasan, mari lihat cara mengendalikan menunggu satu tugas atau banyak tugas.

Menunggu tugasan selesai

Dalam bahagian ini, kita akan membincangkan cara menunggu tugasan selesai, untuk satu atau banyak tugasan. Pengaturcaraan tak segerak adalah berdasarkan fakta bahawa kita boleh meneruskan pelaksanaan program jika kita menjalankan tugas tak segerak. Mungkin ada kalanya anda ingin mengawal aliran dengan lebih baik dan ingin memastikan anda mempunyai hasil yang boleh anda usahakan sebelum meneruskan pelaksanaan program dengan selamat.

Untuk menunggu satu tugas selesai, anda boleh menggunakan asyncio.wait_for. Ia memerlukan dua hujah:

  • boleh ditunggu (diperlukan): Ini ialah coroutine, tugasan atau masa depan yang anda mahu tunggu. Ia boleh menjadi sebarang objek yang boleh ditunggu, seperti panggilan fungsi coroutine, asyncio.Task atau asyncio.Future.

  • tamat masa (pilihan): Ini menentukan bilangan saat maksimum untuk menunggu aw ​​selesai. Jika tamat masa telah dicapai dan yang boleh ditunggu belum selesai, asyncio.wait_for menimbulkan TimeoutError. Jika tamat masa ditetapkan kepada Tiada, fungsi akan menunggu selama-lamanya untuk masa yang ditunggu selesai.

Berikut ialah contoh kaedah ini digunakan:

import asyncio

async def slow_task():
    print("Task started...")
    await asyncio.sleep(5)  # Simulating a long-running task
    print("Task finished!")
    return "Completed"

async def main():
    try:
        # Wait for slow_task to finish within 2 seconds
        result = await asyncio.wait_for(slow_task(), timeout=2)
        print(result)
    except asyncio.TimeoutError:
        print("The task took too long and was canceled!")

asyncio.run(main())

Dalam kod di atas, slow_task() ialah coroutine yang mensimulasikan tugasan yang berjalan lama dengan tidur selama 5 saat. Baris asyncio.wait_for(slow_task(), timeout=2) menunggu untuk tugasan selesai tetapi mengehadkan menunggu kepada 2 saat, menyebabkan tamat masa kerana tugasan mengambil masa yang lebih lama. Apabila tamat masa melebihi, TimeoutError dinaikkan, tugasan dibatalkan dan pengecualian dikendalikan dengan mencetak mesej yang menunjukkan tugasan mengambil masa terlalu lama.

Kami juga boleh menunggu beberapa atau sekumpulan tugasan selesai. Ini boleh dilakukan menggunakan asyncio.wait, asyncio.gather atau asyncio.as_completed. Mari terokai setiap kaedah.

asyncio.tunggu

Kaedah asyncio.wait menunggu koleksi tugasan dan mengembalikan dua set: satu untuk tugasan yang telah selesai dan satu untuk tugasan yang belum selesai. Ia memerlukan hujah berikut:

  • aws (diperlukan, boleh diulang daripada yang boleh ditunggu): Koleksi objek coroutine, tugasan atau masa hadapan yang anda mahu tunggu.

  • tamat masa (terapung atau Tiada, pilihan): Bilangan maksimum saat untuk menunggu. Jika tidak disediakan, ia menunggu selama-lamanya.

  • return_when (malar, pilihan): Menentukan bila asyncio.wait harus kembali. Pilihan termasuk:

    • asyncio.ALL_COMPLETED (default): Returns when all tasks are complete.
    • asyncio.FIRST_COMPLETED: Returns when the first task is completed.
    • asyncio.FIRST_EXCEPTION: Returns when the first task raises an exception.

Let's see how it is used in an example.

import asyncio
import random

async def task():
    await asyncio.sleep(random.uniform(1, 3))

async def main():
    tasks = [asyncio.create_task(task()) for _ in range(3)]
    done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
    print(f"Done tasks: {len(done)}, Pending tasks: {len(pending)}")

asyncio.run(main())

In the code above, asyncio.wait waits for a group of tasks and returns two sets: one with completed tasks and another with those still pending. You can control when it returns, such as after the first task is completed or after all tasks are done. In the example, asyncio.wait returns when the first task is completed, leaving the rest in the pending set.

asyncio.gather

The asyncio.gather method runs multiple awaitable objects concurrently and returns a list of their results, optionally handling exceptions. Let's see the arguments it takes.

  • *aws (required, multiple awaitables): A variable number of awaitable objects (like coroutines, tasks, or futures) to run concurrently.

  • return_exceptions (bool, optional): If True, exceptions in the tasks will be returned as part of the results list instead of being raised.

Let's see how it can be used in an example.

import asyncio
import random

async def task(id):
    await asyncio.sleep(random.uniform(1, 3))
    return f"Task {id} done"

async def main():
    results = await asyncio.gather(task(1), task(2), task(3))
    print(results)

asyncio.run(main())

In the code above, asyncio.gather runs multiple awaitable objects concurrently and returns a list of their results in the order they were passed in. It allows you to handle exceptions gracefully if return_exceptions is set to True. In the example, three tasks are run simultaneously, and their results are returned in a list once all tasks are complete.

asyncio.as_completed

The asyncio.as_completed method is used to return an iterator that yields tasks as they are completed, allowing results to be processed immediately. It takes the following arguments:

  • aws (iterable of awaitables): A collection of coroutine objects, tasks, or futures.

  • timeout (float or None, optional): The maximum number of seconds to wait for tasks to complete. If not provided, it waits indefinitely.

Example

import asyncio
import random

async def task(id):
    await asyncio.sleep(random.uniform(1, 3))
    return f"Task {id} done"

async def main():
    tasks = [task(i) for i in range(3)]
    for coro in asyncio.as_completed(tasks):
        result = await coro
        print(result)

asyncio.run(main())

In the example above, asyncio.as_completed returns an iterator that yields results as each task completes, allowing you to process them immediately. This is useful when you want to handle results as soon as they're available, rather than waiting for all tasks to finish. In the example, the tasks are run simultaneously, and their results are printed as each one finishes, in the order they complete.

So to make a summary, you use:

  • asyncio.wait: when you need to handle multiple tasks and want to track which tasks are completed and which are still pending. It's useful when you care about the status of each task separately.

  • asyncio.gather: when you want to run multiple tasks concurrently and need the results in a list, especially when the order of results matters or you need to handle exceptions gracefully.

  • asyncio.as_completed: when you want to process results as soon as each task finishes, rather than waiting for all tasks to complete. It’s useful for handling results in the order they become available.

However, these methods don't take atomic task management with built-in error handling. In the next section, we will see about asyncio.TaskGroup and how to use it to manage a group of tasks.

asyncio.TaskGroup

asyncio.TaskGroup is a context manager introduced in Python 3.11 that simplifies managing multiple tasks as a group. It ensures that if any task within the group fails, all other tasks are canceled, providing a way to handle complex task management with robust error handling. The class has one method called created_task used to create and add tasks to the task group. You pass a coroutine to this method, and it returns an asyncio.Task object that is managed by the group.

Here is an example of how it is used:

import asyncio

async def task1():
    await asyncio.sleep(1)
    return "Task 1 done"

async def task2():
    await asyncio.sleep(2)
    return "Task 2 done"

async def task_with_error():
    await asyncio.sleep(1)
    raise ValueError("An error occurred")

async def main():
    try:
        async with asyncio.TaskGroup() as tg:
            task1 = tg.create_task(task1())
            task2 = tg.create_task(task2())
            error_task = tg.create_task(task_with_error())
    except Exception as e:
        print(f"Error: {e}")

    # Print results from completed tasks
    print("Task 1 result:", task1.result())
    print("Task 2 result:", task2.result())

asyncio.run(main())

asyncio.TaskGroup manages multiple tasks and ensures that if any task fails, all other tasks in the group are canceled. In the example, a task with an error causes the entire group to be canceled, and only the results of completed tasks are printed.

Usage for this can be in web scraping. You can use asyncio.TaskGroup to handle multiple concurrent API requests and ensure that if any request fails, all other requests are canceled to avoid incomplete data.

We are at the end of the article and we have learned the multiple methods asyncio provides to create and manage tasks. Here is a summary of the methods:

  • asyncio.wait_for: Wait for a task with a timeout.

  • asyncio.wait: Wait for multiple tasks with flexible completion conditions.

  • asyncio.gather: Aggregate multiple tasks into a single awaitable.

  • asyncio.as_completed: Kendalikan tugas apabila ia selesai.

  • asyncio.TaskGroup: Urus sekumpulan tugasan dengan pembatalan automatik apabila gagal.

Kesimpulan

Pengaturcaraan tak segerak boleh mengubah cara anda mengendalikan tugasan serentak dalam Python, menjadikan kod anda lebih cekap dan responsif. Dalam artikel ini, kami telah menavigasi melalui pelbagai kaedah yang disediakan oleh asyncio untuk mencipta dan mengurus tugasan, daripada tamat masa mudah kepada kumpulan tugasan yang canggih. Memahami masa dan cara untuk menggunakan setiap kaedah—asyncio.wait_for, asyncio.wait, asyncio.gather, asyncio.as_completed dan asyncio.TaskGroup—akan membantu anda memanfaatkan potensi penuh pengaturcaraan tak segerak, menjadikan aplikasi anda lebih mantap dan berskala.

Untuk menyelam lebih mendalam ke dalam pengaturcaraan tak segerak dan contoh yang lebih praktikal, terokai panduan terperinci kami di sini.

Jika anda menyukai artikel ini, pertimbangkan untuk melanggan surat berita saya supaya anda tidak terlepas kemas kini masa hadapan.

Selamat mengekod!

Atas ialah kandungan terperinci Mencipta dan Mengurus Tugas dengan Asyncio. 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