Hướng dẫn how do i call a function asynchronously in python? - làm cách nào để gọi một hàm không đồng bộ trong python?

Bạn có thể thực hiện một người trang trí để làm cho các chức năng của bạn không đồng bộ, mặc dù điều đó hơi khó khăn. Mô -đun

@async
def printsum(uid, values):
    summed = 0
    for value in values:
        summed += value

    print("Worker %i: sum value is %i" % (uid, summed))

    return (uid, summed)


if __name__ == '__main__':
    from random import sample

    # The process pool must be created inside __main__.
    async.pool = Pool(4)

    p = range(0, 1000)
    results = []
    for i in range(4):
        result = printsum(i, sample(p, 100))
        results.append(result)

    for result in results:
        print("Worker %i: sum value is %i" % result.get())
4 chứa đầy những điều kỳ quặc nhỏ và những hạn chế dường như tùy tiện - tất cả các lý do nhiều hơn để gói gọn nó đằng sau một giao diện thân thiện.

from inspect import getmodule
from multiprocessing import Pool


def async(decorated):
    r'''Wraps a top-level function around an asynchronous dispatcher.

        when the decorated function is called, a task is submitted to a
        process pool, and a future object is returned, providing access to an
        eventual return value.

        The future object has a blocking get() method to access the task
        result: it will return immediately if the job is already done, or block
        until it completes.

        This decorator won't work on methods, due to limitations in Python's
        pickling machinery (in principle methods could be made pickleable, but
        good luck on that).
    '''
    # Keeps the original function visible from the module global namespace,
    # under a name consistent to its __name__ attribute. This is necessary for
    # the multiprocessing pickling machinery to work properly.
    module = getmodule(decorated)
    decorated.__name__ += '_original'
    setattr(module, decorated.__name__, decorated)

    def send(*args, **opts):
        return async.pool.apply_async(decorated, args, opts)

    return send

Mã dưới đây minh họa việc sử dụng của người trang trí:

@async
def printsum(uid, values):
    summed = 0
    for value in values:
        summed += value

    print("Worker %i: sum value is %i" % (uid, summed))

    return (uid, summed)


if __name__ == '__main__':
    from random import sample

    # The process pool must be created inside __main__.
    async.pool = Pool(4)

    p = range(0, 1000)
    results = []
    for i in range(4):
        result = printsum(i, sample(p, 100))
        results.append(result)

    for result in results:
        print("Worker %i: sum value is %i" % result.get())

Trong một trường hợp trong thế giới thực, tôi sẽ tìm hiểu thêm một chút về người trang trí, cung cấp một số cách để tắt nó để gỡ lỗi (trong khi giữ giao diện trong tương lai) hoặc có thể là một cơ sở để xử lý các ngoại lệ; Nhưng tôi nghĩ rằng điều này thể hiện nguyên tắc đủ tốt.

Ghi chú

Bài đăng này tập trung vào Python 3.5 trở lên, và Async DEF và sự hỗ trợ đang chờ đợi, và sẽ không chạm vào các phương pháp cũ như @asyncio.coroutine.

Như một số bạn có thể nhận thức được, tôi đã dành nhiều tháng trước khi viết lại các kênh hoàn toàn dựa trên Python 3 và các tính năng không đồng bộ của nó (Asyncio).

Khung Async của Python thực sự tương đối đơn giản khi bạn xử lý nó theo mệnh giá, nhưng rất nhiều hướng dẫn và tài liệu thảo luận về chi tiết thực hiện phút, vì vậy tôi muốn đưa ra một tổng quan cấp cao hơn, cố tình bỏ qua một số sự thật nhỏ và tập trung vào Các thực tiễn của các dự án viết kết hợp cả hai loại mã.

Hai thế giới

Phần đầu tiên đó là điều quan trọng nhất cần hiểu - mã Python về cơ bản có thể chạy ở một trong hai "thế giới", hoặc đồng bộ hoặc không đồng bộ. Bạn nên nghĩ về chúng là tương đối riêng biệt, có các thư viện và kiểu gọi khác nhau nhưng chia sẻ các biến và cú pháp.

Bạn không thể sử dụng thư viện cơ sở dữ liệu đồng bộ như MySQL-Python trực tiếp từ mã Async; Tương tự, bạn không thể sử dụng thư viện Redis Async như Aioredis trực tiếp từ mã đồng bộ hóa. Có nhiều cách để làm cả hai, nhưng bạn cần phải vượt qua thế giới khác để chạy mã từ nó.

Trong thế giới đồng bộ, con trăn tồn tại trong nhiều thập kỷ, bạn gọi các chức năng trực tiếp và mọi thứ được xử lý như được viết trên màn hình. Tùy chọn tích hợp duy nhất của bạn để chạy mã song song trong cùng một quy trình là các luồng.

Trong thế giới không đồng bộ, mọi thứ thay đổi xung quanh một chút. Mọi thứ chạy trên một vòng lặp sự kiện trung tâm, đó là một chút mã cốt lõi cho phép bạn chạy một số coroutines cùng một lúc. Coroutines chạy đồng bộ cho đến khi họ gặp phải và sau đó họ dừng lại, từ bỏ quyền kiểm soát vòng lặp sự kiện và một cái gì đó khác có thể xảy ra.

Điều này có nghĩa là các hàm/vật phẩm gọi đồng bộ và không đồng bộ là các loại khác nhau - bạn không thể chỉ trộn và kết hợp chúng. Cố gắng chờ đợi một chức năng đồng bộ hóa và bạn sẽ thấy Python phàn nàn, quên chờ một chức năng Async và bạn sẽ lấy lại một đối tượng Coroutine thay vì kết quả bạn muốn.

Bạn thực sự không cần phải biết các chi tiết tốt về cách tất cả hoạt động để sử dụng nó - chỉ cần biết rằng các coroutines phải từ bỏ quyền kiểm soát rõ ràng thông qua một sự chờ đợi. Điều này khác với các chủ đề hoặc Greenlets, có thể chuyển đổi ngữ cảnh bất cứ lúc nào.

Nếu bạn chặn một coroutine đồng bộ - có thể bạn sử dụng thời gian.s ngủ (10) thay vì chờ đợi asyncio.s ngủ (10) - bạn không trả lại điều khiển cho vòng lặp sự kiện và bạn sẽ giữ toàn bộ quá trình và không có gì khác có thể xảy ra. Về mặt tích cực, không có gì khác có thể chạy trong khi mã của bạn đang di chuyển từ một cuộc gọi đang chờ đến bên tiếp theo, làm cho điều kiện cuộc đua khó khăn hơn.

Điều này được gọi là đa nhiệm hợp tác, và trong khi nó có nhiều mặt, chế độ thất bại im lặng này là vấn đề thiết kế chính của nó. Nếu bạn sử dụng một cuộc gọi đồng bộ chặn do nhầm lẫn, không có gì sẽ thất bại rõ ràng, nhưng mọi thứ sẽ chỉ chạy chậm một cách bí ẩn. Python có chế độ gỡ lỗi sẽ cảnh báo bạn về những thứ chặn quá lâu, cùng với các lỗi phổ biến khác, nhưng hãy nhận thức được một điều - viết mã không đồng bộ rõ ràng là khó hơn là viết mã đồng bộ.

Đây là một trong những lý do các kênh cung cấp cho bạn sự lựa chọn, thay vì buộc bạn phải luôn viết Async; Mã đồng bộ hóa dễ viết hơn, nói chung là an toàn hơn và có nhiều thư viện hơn để lựa chọn.

Bạn nên nghĩ về cơ sở mã của mình như bao gồm các phần của mã đồng bộ hóa hoặc mã async - bất cứ thứ gì bên trong Async def là mã ASYNC, bất cứ thứ gì khác (bao gồm cả phần thân chính của tệp python hoặc lớp) là mã đồng bộ. Đáng chú ý, __init__ phải luôn đồng bộ ngay cả khi tất cả các phương pháp của lớp là không đồng bộ.

Các cuộc gọi chức năng

Vì vậy, bây giờ chúng ta đã xem xét hai thế giới khác nhau, chúng ta hãy nhìn vào điều chính có thể kết nối giữa chúng - các cuộc gọi chức năng. Bên trong hàm Async hoặc Sync, bạn có thể viết mã cơ bản là bình thường, nhưng ngay khi bạn gọi một hàm, bạn có khả năng chuyển đổi.

Có bốn trường hợp:

  • Gọi mã đồng bộ từ mã đồng bộ. Đây chỉ là một cuộc gọi chức năng bình thường - như thời gian.s ngủ (10). Không có gì rủi ro hoặc đặc biệt về điều này.
  • Gọi mã Async từ mã Async. Bạn phải sử dụng chờ đợi ở đây, vì vậy bạn sẽ chờ đợi asyncio.s ngủ (10)
  • Gọi mã đồng bộ từ mã Async. Bạn có thể làm điều này, nhưng như tôi đã nói ở trên, nó sẽ chặn toàn bộ quá trình và làm cho mọi thứ chậm chạp một cách bí ẩn, và bạn không nên. Thay vào đó, bạn cần cung cấp mã đồng bộ hóa chủ đề của riêng mình.
  • Gọi mã Async từ mã đồng bộ. Thậm chí thậm chí sử dụng đang chờ đợi bên trong hàm đồng bộ là lỗi cú pháp trong Python, vì vậy để làm điều này, bạn cần tạo một vòng lặp sự kiện để mã chạy bên trong.

Hãy đi sâu hơn vào từng trường hợp và những gì đang thực sự xảy ra.

Đồng bộ hóa từ đồng bộ hóa

Đây là Python tiêu chuẩn - bạn gọi một đối tượng, các khối python và di chuyển vào mã được gọi là, chạy nó, sau đó trả lại kết quả cho người gọi và bỏ chặn nó.

Async từ Async

Bây giờ, mọi thứ bắt đầu trở nên thú vị. Khi bạn có một chức năng không đồng bộ (coroutine) trong Python, bạn sẽ khai báo nó với Async def, điều này thay đổi cách thức hoạt động của cuộc gọi của nó.

Cụ thể, gọi nó sẽ ngay lập tức trả lại một đối tượng coroutine, về cơ bản nói rằng "Tôi có thể chạy coroutine với các đối số bạn gọi và trả về kết quả khi bạn chờ đợi tôi".

Mã trong hàm đích chưa được gọi - đây chỉ là một lời hứa rằng mã sẽ chạy và bạn sẽ nhận được kết quả trở lại, nhưng bạn cần đưa nó vào vòng lặp sự kiện để làm điều đó.

May mắn thay, Python có một tuyên bố tích hợp để đưa ra một coroutine cho vòng lặp sự kiện và lấy lại kết quả - chờ đợi. Điều đó có nghĩa là khi bạn nói Await Asyncio.s ngủ (10), bạn đang thực hiện một lời hứa rằng chức năng giấc ngủ sẽ chạy, và sau đó chuyển nó lên vòng lặp sự kiện và chờ kết quả. Hãy xem xét một ví dụ:

Ghi chú

Ví dụ này có một giấc ngủ có chủ ý trong đó để thể hiện hoạt động chặn - như nói chuyện với cơ sở dữ liệu.

async def get_chat_id(name):
    await asyncio.sleep(3)
    return "chat-%s" % name

async def main():
    result = await get_chat_id("django")

Khi bạn gọi chờ, chức năng bạn đang bị đình chỉ trong khi bất cứ điều gì bạn yêu cầu chờ đợi xảy ra, và sau đó khi nó kết thúc, vòng lặp sự kiện sẽ đánh thức chức năng trở lại và tiếp tục từ cuộc gọi đang chờ, truyền bất kỳ kết quả nào. Trong ví dụ ở đây, hàm chính () tạm dừng và cung cấp kiểm soát lại cho vòng lặp sự kiện, nhận thấy rằng get_chat_id cần chạy, gọi nó, và sau đó gọi là đang chờ đợi và bị treo với một điểm đánh dấu để tiếp tục trong ba giây. Khi nó tiếp tục, GET_CHAT_ID hoàn thành, trả về kết quả và sau đó điều đó làm cho Main sẵn sàng chạy lại và vòng lặp sự kiện tiếp tục với giá trị được trả về.

Tất nhiên, đây là Python, vì vậy đang chờ đợi không chỉ là một tuyên bố kỳ diệu chỉ hoạt động trên các chức năng - nó cần bất cứ điều gì có thể chờ đợi. Điều thực sự xảy ra là khi bạn gọi một chức năng không đồng bộ, nó sẽ trả về một đối tượng coroutine, và sau đó bạn có thể vượt qua điều đó để chờ ngủ coroutine hiện tại của bạn cho đến khi bạn yêu cầu chờ đợi trên đường, như thế này: như thế này:

async def get_chat_id(name):
    await asyncio.sleep(3)
    return "chat-%s" % name

async def main():
    id_coroutine = get_chat_id("django")
    result = await id_coroutine

Đây là cách mà mã Async có thể có rất nhiều điều xảy ra cùng một lúc - bất cứ điều gì chặn các cuộc gọi đang chờ đợi và được đưa vào danh sách các coroutines tạm dừng của sự kiện để một cái gì đó khác có thể chạy. Tất cả mọi thứ được tạm dừng đều có một kích hoạt liên quan sẽ đánh thức lại nó-một số dựa trên thời gian, một số dựa trên mạng và hầu hết chúng giống như ví dụ trên và chờ kết quả từ một coroutine khác.

Đó là những hoạt động không chờ đợi các coroutines khác cho phép thực sự Async, nhờ vào vòng lặp sự kiện - có một số điều mà nó có thể làm mà không cần một Coroutine Python phải quản lý chúng, như chờ đợi một khoảng thời gian để vượt qua hoặc chờ byte xuất hiện trên ổ cắm mạng.

Nếu bạn yêu cầu chờ đợi một trong những hoạt động cấp thấp này, tất cả các coroutines Python của bạn đều bị đình chỉ trong khi vòng lặp sự kiện thực hiện phép thuật và lắng nghe tất cả các ổ cắm và bộ đếm thời gian, nhờ một số cuộc gọi hệ thống thông minh và chương trình khác tạo ra tôi đau đầu quá. Vòng lặp sự kiện thực sự là những gì làm cho mọi thứ có thể, và không có nó, Async Python sẽ chỉ là một dòng điều khiển siêu kỳ lạ mà không có lợi ích tốc độ thực tế.

Đồng bộ hóa từ Async

Bạn có nhớ khi tôi nói điều này là nguy hiểm một vài phần trước đây không? Ý tôi là nó. Bởi vì chờ đợi chỉ là một tuyên bố Python mà bạn có thể chuyển một coroutine và lấy lại kết quả, điều đó có nghĩa là bạn tự do bỏ qua nó và chỉ gọi một chức năng tốt, cũ, đồng bộ, như thế này:

def get_chat_id(name):
    time.sleep(3)
    return "chat-%s" % name

async def main():
    result = get_chat_id("django")

Làm điều đó, và bạn không cung cấp cho vòng lặp sự kiện bất kỳ cơ hội nào để chạy - bạn đã không tạm dừng coroutine hiện tại và cung cấp điều khiển vòng lặp sự kiện đang chờ đợi. Điều đó có nghĩa là mọi coroutine khác có thể muốn chạy - có thể một người có một số byte đang chờ nó trên ổ cắm, hoặc một cái khác đã ngủ trong vài giây - thậm chí không có cơ hội, và coroutine của bạn chỉ bỏ qua tất cả và giữ Chạy mã đồng bộ. Vòng lặp sự kiện không có một sức mạnh đặc biệt bên trong Python để làm gián đoạn bạn, nó cần bạn mang lại quyền kiểm soát trở lại.

Bây giờ, có một sự khác biệt tinh tế ở đây giữa các cuộc gọi chặn và không chặn. Sẽ không làm hỏng ngày của bạn nếu bạn gọi một chức năng đồng bộ không chặn, như thế này:

def get_chat_id(name):
    return "chat-%s" % name

async def main():
    result = get_chat_id("django")

Tuy nhiên, nếu bạn gọi một chức năng chặn, như Django ORM, mã bên trong hàm async sẽ trông giống hệt nhau, nhưng bây giờ mã nguy hiểm có thể chặn toàn bộ vòng lặp sự kiện vì nó không chờ đợi:

def get_chat_id(name):
    return Chat.objects.get(name=name).id

async def main():
    result = get_chat_id("django")

Bạn có thể thấy làm thế nào dễ dàng để có một chức năng không chặn mà "vô tình" sẽ bị chặn nếu một lập trình viên không nhận thức được tất cả mọi thứ gọi nó. Đây là lý do tại sao tôi khuyên bạn không bao giờ gọi bất cứ điều gì đồng bộ từ chức năng Async mà không làm điều đó một cách an toàn hoặc không biết trước đó là chức năng thư viện tiêu chuẩn không chặn, như Os.Path.Join.

Nhưng điều gì là an toàn? Trong thế giới đồng bộ, luồng là tùy chọn tích hợp duy nhất của chúng tôi cho sự đồng thời, vì vậy những gì chúng tôi có thể làm là quay một chủ đề mới, có chức năng đồng bộ hóa chạy bên trong nó, và sau đó có sự tạm dừng của chúng tôi và kiểm soát lại vòng lặp sự kiện Cho đến khi chủ đề của nó kết thúc và có một kết quả.

Python có cách thực hiện tích hợp này một chút, dưới dạng người thực thi:

def get_chat_id(name):
    return Chat.objects.get(name=name).id

async def main():
    executor = concurrent.futures.ThreadPoolExecutor(max_workers=3)
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(executor, get_chat_id, "django")

Đây là logic cấp thấp đẹp nếu bạn cần, nhưng tôi muốn có một cách đơn giản hơn cho người dùng Kênh, và vì vậy trong gói Asgiref.sync, cung cấp tất cả các trình trợ giúp ASGI cấp thấp, có hai chức năng rất tiện dụng-SYNC_TO_ASYNC và async_to_sync.

Chúng tôi sẽ đến Async_to_sync sau, nhưng đây là cách bạn gọi chức năng đồng bộ với SYNC_TO_ASYNC:

Ghi chú

Trong các kênh, có một biến thể gọi là DataBase_Sync_TO_ASYNC đảm bảo các kết nối DB được làm sạch, vì vậy bạn thực sự sẽ sử dụng nó trong ví dụ này.

def get_chat_id(name):
    return Chat.objects.get(name=name).id

async def main():
    result = await sync_to_async(get_chat_id)("django")

Bạn cũng có thể sử dụng nó như một người trang trí:

@sync_to_async
def get_chat_id(name):
    return Chat.objects.get(name=name).id

async def main():
    result = await get_chat_id("django")

Nó gói gọn sự lan truyền ngoại lệ, xử lý kết quả và luồn cho bạn vào một giao diện đơn giản hoạt động một bộ trang trí chức năng, bộ trang trí phương pháp và trình bao bọc trực tiếp. Nó khá đơn giản, không giống như async_to_sync, nhưng nó vẫn giảm một vài dòng xuống một và làm cho việc vô tình khó khăn hơn nhiều.

Async từ đồng bộ

Đây là nơi mọi thứ trở nên khó khăn. Các chức năng ASYNC, theo bản chất của chúng, cần phải chạy bên trong một vòng lặp sự kiện - một cái gì đó phải có để xử lý các cuộc gọi đang chờ đợi của họ, và dịch vụ những thứ như ổ cắm và bộ hẹn giờ. Tuy nhiên, nếu bạn đang ở trong bối cảnh đồng bộ, bạn không thể chờ đợi. Hãy thử nó:

@async
def printsum(uid, values):
    summed = 0
    for value in values:
        summed += value

    print("Worker %i: sum value is %i" % (uid, summed))

    return (uid, summed)


if __name__ == '__main__':
    from random import sample

    # The process pool must be created inside __main__.
    async.pool = Pool(4)

    p = range(0, 1000)
    results = []
    for i in range(4):
        result = printsum(i, sample(p, 100))
        results.append(result)

    for result in results:
        print("Worker %i: sum value is %i" % result.get())
0

Bất kỳ từ khóa Async nào - Await, Async với và nhiều thứ khác, là cú pháp thực tế nếu bạn tuyên bố chúng bên trong hàm đồng bộ, cơ thể lớp hoặc tệp.

Vậy ta phải làm sao? Vâng, chúng tôi cần một vòng lặp sự kiện, vì vậy chúng tôi phải làm một vòng. Bí mật đằng sau sự hỗ trợ của Async của Python là nó chỉ là một vòng lặp sự kiện chạy trên đỉnh của Python tốt, cũ, đồng bộ. Đây là cách chạy chức năng Async như ở trên:

@async
def printsum(uid, values):
    summed = 0
    for value in values:
        summed += value

    print("Worker %i: sum value is %i" % (uid, summed))

    return (uid, summed)


if __name__ == '__main__':
    from random import sample

    # The process pool must be created inside __main__.
    async.pool = Pool(4)

    p = range(0, 1000)
    results = []
    for i in range(4):
        result = printsum(i, sample(p, 100))
        results.append(result)

    for result in results:
        print("Worker %i: sum value is %i" % result.get())
1

Phương thức Run_until_complete trên một vòng lặp giống như phiên bản đồng bộ của Await - nó sẽ lấy một coroutine, hãy để vòng lặp sự kiện làm việc của nó, và sau đó quay lại khi hoàn thành. Chúng ta phải gọi SET_EVENT_LOOP là có trạng thái toàn cầu bên trong Asyncio theo dõi vòng lặp hiện tại là gì, vì vậy khi bạn gọi asyncio.s ngủ (), bạn không phải truyền nó một vòng lặp = đối số mỗi lần.

Bạn sẽ thấy một mẫu như ở trên trong rất nhiều ví dụ trực tuyến và đó là một cách tốt để chỉ chạy một chương trình Async một phát từ một tập lệnh. Thậm chí tốt hơn, Python 3.7 sẽ bọc ở trên bên trong của một người trợ giúp gọi là asyncio.run!

Nhưng điều gì sẽ xảy ra nếu bạn đang viết một ứng dụng phức tạp và bạn cần gọi mã async từ mã đồng bộ hóa của bạn đã ở trong mã async khác? (Đưa ra hiệu ứng âm thanh còi khởi động!)

Nếu bạn bên trong mã đồng bộ hóa chạy bên trong mã Async, thì hy vọng, bạn sẽ ở một luồng như chúng ta đã thảo luận ở trên. Điều đó có nghĩa là bạn cần phải nhảy ra khỏi chuỗi, tìm vòng lặp sự kiện chính và chạy một cái gì đó ở đó, trong khi chặn luồng cho đến khi nhiệm vụ không đồng bộ mà bạn gửi đến vòng lặp sự kiện kết thúc. Vâng, đó là phức tạp để lập trình như nó nghe.

Một điều khác bạn có thể làm là tạo một vòng lặp sự kiện mới. Hãy xem, các vòng lặp sự kiện Asyncio là toàn cầu - điều đó có nghĩa là về lý thuyết, bạn có thể chạy một vòng lặp sự kiện nhỏ bên trong chuỗi của bạn chỉ để phục vụ chức năng Async mà bạn cần, sau đó đóng lại. Tôi không muốn làm điều này, vì có quá nhiều vòng lặp sự kiện chạy cùng một lúc làm cho đầu tôi bị tổn thương và tôi không chắc đó là một ý kiến ​​hay.

Do đó, chúng tôi có Asgiref.sync.async_to_sync. Bao bọc tiện dụng này sẽ làm tất cả những điều trên, cộng với sự lan truyền giá trị ngoại lệ và trả lại, cộng với phát hiện nếu bạn làm điều gì đó ngu ngốc (như cố gắng sử dụng nó từ một chủ đề đã có một vòng lặp sự kiện chạy trong đó). Tất cả những gì bạn cần làm là bọc chức năng và chuyển bất kỳ đối số nào:

@async
def printsum(uid, values):
    summed = 0
    for value in values:
        summed += value

    print("Worker %i: sum value is %i" % (uid, summed))

    return (uid, summed)


if __name__ == '__main__':
    from random import sample

    # The process pool must be created inside __main__.
    async.pool = Pool(4)

    p = range(0, 1000)
    results = []
    for i in range(4):
        result = printsum(i, sample(p, 100))
        results.append(result)

    for result in results:
        print("Worker %i: sum value is %i" % result.get())
2

Nó cũng hoạt động như một người trang trí:

@async
def printsum(uid, values):
    summed = 0
    for value in values:
        summed += value

    print("Worker %i: sum value is %i" % (uid, summed))

    return (uid, summed)


if __name__ == '__main__':
    from random import sample

    # The process pool must be created inside __main__.
    async.pool = Pool(4)

    p = range(0, 1000)
    results = []
    for i in range(4):
        result = printsum(i, sample(p, 100))
        results.append(result)

    for result in results:
        print("Worker %i: sum value is %i" % result.get())
3

Nội bộ của async_to_sync phức tạp hơn một chút so với sync_to_async, nhưng điều quan trọng là đó là một trình bao bọc/bộ trang trí mà bạn có thể sử dụng trong hầu hết các bối cảnh và nó sẽ làm đúng, và thử và giữ một vòng lặp sự kiện trong của bạn trong quá trình.

Chờ đợi là bạn của bạn

Tại thời điểm này, hy vọng bạn có một sự hiểu biết về cách hai loại chức năng hoạt động. Các chức năng ASYNC cần chạy trên một vòng lặp sự kiện và trong khi chúng có thể gọi các hàm đồng bộ, nó rất nguy hiểm. Các chức năng đồng bộ hóa chỉ cần chạy trên trăn trần và để chúng gọi các chức năng không đồng bộ, bạn cần tìm hoặc tạo vòng lặp sự kiện để chạy mã.

Khung hoặc tệp chương trình bạn đang chạy bên trong xác định "chế độ chính" của chương trình là gì. Nhiều người sẽ chạy đồng bộ theo mặc định, và vì vậy nếu bạn muốn làm điều gì đó Async (giả sử, tìm nạp mười URL song song), bạn sẽ cần phải rơi vào Async và quay một vòng lặp sự kiện cho phần mã nhỏ đó. Một số cái mới hơn sẽ có một vòng lặp sự kiện chạy trong luồng chính và chạy mọi thứ theo mặc định - như các kênh - vì vậy khi bạn muốn nói chuyện với API đồng bộ, bạn cần phải quay một luồng và gọi nó.

Khi bạn đang viết mã Async - trong các kênh hoặc ở nơi khác - điều bạn thực sự cần xem xét là "Tôi có gọi mã đồng bộ ở đây do nhầm lẫn không?". Nó có thể là những thứ khác ngoài các cuộc gọi chức năng rõ ràng - hãy nhớ rằng, các nhà quản lý ngữ cảnh gọi các phương thức __enter__ và __exit__ và cho các vòng lặp gọi các phương thức __iter__. Lặp lại trên một truy vấn Django trong hàm Async sẽ thực hiện chặn các hoạt động đồng bộ và chặn vòng lặp sự kiện của bạn!

Hầu hết trong số này có các tương đương không đồng bộ trong Python 3.5/3.6 - ví dụ, có sự không đồng bộ với các nhà quản lý bối cảnh không đồng bộ và không đồng bộ cho các trình lặp không đồng bộ - nhưng chúng yêu cầu các đối tượng bạn sử dụng chúng đã cung cấp các hoạt động không đồng bộ của các hoạt động đó ( như xác định phương thức __Aiter__).

Như với câu chuyện ngay từ đầu, điều này chỉ làm nổi bật rằng các API đồng bộ và đồng bộ là các triển khai khác nhau, không tương thích. Một thư viện có thể cung cấp cả hai, nhưng điều đó có nghĩa là viết hai đường dẫn mã hoàn toàn riêng biệt và thường là hai cơ chế mạng nội bộ riêng biệt, đó là lý do tại sao bạn thường thấy các thư viện khác nhau để đồng bộ hóa so với async.

Tuy nhiên, có một cách khác. Một số phần cốt lõi của các kênh chỉ cung cấp API không đồng bộ - đáng chú ý nhất là các lớp kênh - và chúng tôi dựa vào sự hiện diện của async_to_sync để cho phép bất cứ ai viết mã đồng bộ gọi chúng dễ dàng. Điều này cũng có thể được áp dụng cho mã khác - trên thực tế, một số thư viện đã có loại chức năng này trong nội bộ.

Thật không may, nếu không phải là một khái niệm Python cốt lõi, bạn sẽ phải thực hiện ASGIREF như một sự phụ thuộc để sử dụng các chức năng này, nhưng đó là một gói nhẹ có chủ ý và tôi khuyến khích bạn sử dụng nó nếu bạn là người bảo trì thư viện. Điều quan trọng tôi đề nghị là viết triển khai cơ sở theo kiểu Async, và sau đó sử dụng async_to_sync để cung cấp các phiên bản đồng bộ của API khi cần thiết.

Về mặt lý thuyết, đó cũng là những loại giấy gói này có thể, chúng ta hãy bắt đầu viết lại các phần của một khung lớn như Django là không đồng bộ nhưng vẫn cho phép tương thích ngược. Tôi chưa có bất kỳ kế hoạch thực sự nào cho điều đó, nhưng đôi khi đó chắc chắn là điều tôi nghĩ về; Tôi không muốn chúng ta phải phá vỡ thế giới Python thành hai, thế giới đồng bộ hóa và thế giới không đồng bộ, khi một trong những thế mạnh chính của chúng ta là hơi thở và chiều sâu của các thư viện và ràng buộc có sẵn.

Nhìn sâu hơn

Có rất nhiều chi tiết kỹ thuật mà tôi chưa tham gia ở đây - về cách các vòng lặp sự kiện thực sự dựa trên máy phát điện, hoặc cách sử dụng các nhiệm vụ và tương lai - nhưng tôi muốn thử và có ý tưởng cơ bản về những gì đang xảy ra, và Cách tôi cố gắng suy nghĩ về những vấn đề này (đặc biệt hữu ích khi bạn đang cố gắng gỡ lỗi mã Async). Chỉ cần lưu ý rằng có những thứ khác ngoài các coroutines có thể chờ đợi - giống như có những thứ khác ngoài các chức năng trong Python có thể gọi được!

Python 3.7 sẽ mang lại một số cải tiến giúp điều này - đáng chú ý nhất là asyncio.run - nhưng nó vẫn còn hạn chế và không giải quyết toàn bộ vấn đề. Tôi không chắc liệu cách tiếp cận mà tôi đã thực hiện cho các kênh - và các trợ giúp Sync_to_async và async_to_sync đến từ nó - sẽ làm việc cho các chương trình và thư viện Python nói chung, nhưng cho đến nay tôi vẫn chưa tìm thấy lý do .

Tôi cũng sẽ khuyến khích bạn phát triển các ứng dụng của mình với Pythonasynciodebug đã bật - nó sẽ phát hiện các coroutines bị chặn, các coroutines mà bạn quên chờ và một số trường hợp cạnh khác có thể khó bắt được.

Hy vọng rằng bạn đã tìm thấy cách tiếp cận này với chức năng Async của Python hữu ích và nếu điều này khiến bạn phấn khích và bạn muốn viết một số mã web không đồng bộ, tôi khuyến khích bạn kiểm tra các kênh. Nếu bạn là một tác giả thư viện hoặc khung và muốn trò chuyện về ý nghĩa của việc sống ở cả hai thế giới - đồng bộ hóa và async - xin vui lòng chọc tôi, tôi luôn sẵn lòng trò chuyện về những vấn đề này!

Làm thế nào để bạn gọi một chức năng không đồng bộ?

Async và chờ đợi bên trong hàm Async, bạn có thể sử dụng từ khóa đang chờ đợi trước khi gọi đến một hàm trả về lời hứa. Điều này làm cho mã chờ vào thời điểm đó cho đến khi lời hứa được giải quyết, tại thời điểm đó, giá trị hoàn thành của lời hứa được coi là giá trị trả lại hoặc giá trị bị từ chối được ném.use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.

Python có chức năng không đồng bộ không?

Asyncio được sử dụng làm nền tảng cho nhiều khung không đồng bộ của Python cung cấp mạng hiệu suất cao và máy chủ web, thư viện kết nối cơ sở dữ liệu, hàng đợi nhiệm vụ phân tán, v.v. that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc. asyncio is often a perfect fit for IO-bound and high-level structured network code.

Làm thế nào để bạn gọi chức năng async mà không chờ đợi python?

Chức năng của Python Run Run Async mà không chờ câu trả lời mã..
nhập khẩu asyncio ..
async def main ():.
In ('Xin chào ...').
chờ đợi asyncio.Ngủ (1).
In ('... thế giới!').
# Python 3.7+.

Làm thế nào để bạn bắt đầu chủ đề không đồng bộ trong Python?

Thực thi không đồng bộ (async)..
Thêm từ khóa Async trước các khai báo chức năng của bạn để làm cho chúng có thể chờ đợi ..
Thêm từ khóa đang chờ đợi khi bạn gọi các chức năng Async của mình (không có nó, họ sẽ không chạy) ..
Tạo các tác vụ từ các chức năng Async mà bạn muốn bắt đầu không đồng bộ.Cũng đợi kết thúc của họ ..
Gọi Asyncio ..