Gọi hàm async từ python chính

Với lập trình bất đồng bộ, chúng ta có thể thực thi các tác vụ đồng thời với việc thực thi chương trình chính. Các từ khóa không đồng bộ và chờ đợi đơn giản hóa việc lập trình không đồng bộ trong Python. Python có mô hình lập trình không đồng bộ được tích hợp trong ngôn ngữ

Coroutine là một chức năng Python được sử dụng trong đa nhiệm hợp tác, nơi chúng có thể bị tạm dừng và tiếp tục. Một coroutine được khai báo với cú pháp

async def mul[x, y]:
    return x * y
0

Các từ khóa async/await

Các từ khóa

async def mul[x, y]:
    return x * y
1 đã được chuẩn hóa trong Python 3. 7. Họ đơn giản hóa lập trình không đồng bộ trong Python. Từ khóa
async def mul[x, y]:
    return x * y
2 được sử dụng để tạo quy trình đăng ký Python. Từ khóa
async def mul[x, y]:
    return x * y
3 tạm dừng thực thi quy trình đăng ký cho đến khi nó hoàn thành và trả về dữ liệu kết quả. Các từ khóa chờ đợi chỉ hoạt động trong chức năng không đồng bộ

Sau đây là một chương trình đơn giản sử dụng từ khóa

async def mul[x, y]:
    return x * y
1

#!/usr/bin/python

import asyncio

async def mul[x, y]:
    return x * y

loop = asyncio.get_event_loop[]

res = loop.run_until_complete[mul[5, 5]]
print[res2]

loop.close[]

Chương trình tạo và chạy một chức năng không đồng bộ

async def mul[x, y]:
    return x * y

Một coroutine là một hàm được khai báo với công cụ sửa đổi

async def mul[x, y]:
    return x * y
2

________số 8_______

async def mul[x, y]:
    return x * y
6 trả về vòng lặp sự kiện asyncio. Cần có một vòng lặp sự kiện để thực thi mã không đồng bộ

res = loop.run_until_complete[mul[5, 5]]

Hàm

async def mul[x, y]:
    return x * y
7 chạy vòng lặp sự kiện cho đến khi hoàn thành một tương lai. Nó trả về kết quả của tương lai hoặc tăng ngoại lệ của nó. Tương lai đại diện cho kết quả cuối cùng của hoạt động không đồng bộ

Python async/await ví dụ II

Với

async def mul[x, y]:
    return x * y
8, chúng tôi đơn giản hóa mã. Hàm tạo một vòng lặp sự kiện, lên lịch cho các coroutines và cuối cùng đóng vòng lặp

#!/usr/bin/python

import asyncio

async def add[x, y]:
    return x + y

async def get_results[]:
    res1 = await add[3, 4]
    res2 = await add[8, 5]

    print[res1, res2]

asyncio.run[get_results[]]

Trong ví dụ, chúng tôi chạy hai hàm async

$ python simple2.py 
7 13

Thu thập là một cách thuận tiện để lên lịch cho nhiều coroutine chạy đồng thời. Chúng tôi thu thập các coroutines với

async def mul[x, y]:
    return x * y
9

Với

loop = asyncio.get_event_loop[]
0, chúng tôi tạo một coroutine hoàn thành trong số giây đã chỉ định. Nó thường được sử dụng để mô phỏng một nhiệm vụ chạy dài

#!/usr/bin/python

import asyncio
import time
import random

async def task1[]:

    wait = random.randint[0, 3]
    await asyncio.sleep[wait]
    print["task 1 finished"]


async def task2[]:

    wait = random.randint[0, 3]
    await asyncio.sleep[wait]
    print["task 2 finished"]


async def task3[]:

    wait = random.randint[0, 3]
    await asyncio.sleep[wait]
    print["task 3 finished"]


async def main[]:

    for x in range[2]:
        await asyncio.gather[task1[], task2[], task3[]]
        time.sleep[1]
        print['----------------------------']

t1 = time.perf_counter[]
asyncio.run[main[]]
t2 = time.perf_counter[]

print[f'Total time elapsed: {t2-t1:0.2f} seconds']

Chúng tôi có ba nhiệm vụ hoàn thành trong một số giây ngẫu nhiên. Bằng cách này, chúng tôi mô phỏng việc thực hiện ba tác vụ dài hạn khác nhau

async def main[]:

    for x in range[2]:
        await asyncio.gather[task1[], task2[], task3[]]
        print['----------------------------']

Các chức năng chính tập hợp tất cả ba nhiệm vụ. Nó cũng là một coroutine, được trang trí bằng

async def mul[x, y]:
    return x * y
2. Chúng tôi đang chờ kết quả của
async def mul[x, y]:
    return x * y
9 với từ khóa
async def mul[x, y]:
    return x * y
3

$ python simple3.py 
task 2 finished
task 1 finished
task 3 finished
----------------------------
task 3 finished
task 2 finished
task 1 finished
----------------------------
Total time elapsed: 8.01 seconds

Ví dụ không đồng bộ Python Playwright

Nhiều thư viện có hỗ trợ lập trình không đồng bộ. Thư viện MS Playwright cho phép tự động hóa các trình duyệt ở cả chế độ đồng bộ và không đồng bộ

Lập trình không đồng bộ đã đạt được rất nhiều sức hút trong vài năm qua và vì lý do chính đáng. Mặc dù nó có thể khó hơn kiểu tuyến tính truyền thống, nhưng nó cũng hiệu quả hơn nhiều

Ví dụ: thay vì đợi một yêu cầu HTTP kết thúc trước khi tiếp tục thực hiện, với các coroutine không đồng bộ của Python, bạn có thể gửi yêu cầu và thực hiện các công việc khác đang chờ trong hàng đợi trong khi chờ yêu cầu HTTP kết thúc. Có thể cần suy nghĩ thêm một chút để hiểu đúng logic, nhưng bạn sẽ có thể xử lý nhiều công việc hơn với ít tài nguyên hơn

Ngay cả khi đó, cú pháp và việc thực thi các hàm không đồng bộ trong các ngôn ngữ như Python thực sự không khó lắm. Bây giờ, JavaScript là một câu chuyện khác, nhưng Python dường như thực thi nó khá tốt

Tính không đồng bộ dường như là một lý do lớn tại sao Node. js rất phổ biến cho lập trình phía máy chủ. Phần lớn mã chúng tôi viết, đặc biệt là trong các ứng dụng IO nặng như trang web, phụ thuộc vào tài nguyên bên ngoài. Đây có thể là bất cứ thứ gì từ cuộc gọi cơ sở dữ liệu từ xa đến POST tới dịch vụ REST. Ngay khi bạn yêu cầu bất kỳ tài nguyên nào trong số này, mã của bạn sẽ chờ xung quanh mà không có gì để làm

Với lập trình không đồng bộ, bạn cho phép mã của mình xử lý các tác vụ khác trong khi chờ các tài nguyên khác này phản hồi

quân đoàn

Một hàm không đồng bộ trong Python thường được gọi là 'coroutine', đây chỉ là một hàm sử dụng từ khóa async hoặc một hàm được trang trí bằng @asyncio.coroutine. Một trong các chức năng dưới đây sẽ hoạt động như một quy trình đăng quang và có hiệu quả tương đương về loại

import asyncio

async def ping_server[ip]:
    pass

@asyncio.coroutine
def load_file[path]:
    pass

Đây là những hàm đặc biệt trả về các đối tượng coroutine khi được gọi. Nếu bạn đã quen thuộc với JavaScript Promises, thì bạn có thể coi đối tượng được trả về này gần giống như một Promise. Gọi một trong hai điều này không thực sự chạy chúng, mà thay vào đó, một đối tượng được trả về, sau đó có thể được chuyển đến vòng lặp sự kiện để được thực thi sau này

Trong trường hợp bạn cần xác định xem một chức năng có phải là một coroutine hay không, thì

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
0 sẽ cung cấp phương thức thực hiện chính xác điều này cho bạn. Hoặc, nếu bạn cần xác định xem một đối tượng được trả về từ một hàm có phải là một đối tượng coroutine hay không, bạn có thể sử dụng thay thế

Năng suất từ

Có một vài cách để thực sự gọi một coroutine, một trong số đó là phương thức

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
3. Điều này đã được giới thiệu trong Python 3. 3 và đã được cải thiện hơn nữa trong Python 3. 5 ở dạng
import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
4 [chúng ta sẽ đề cập sau]

Biểu thức

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
3 có thể được sử dụng như sau

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']

Như bạn có thể thấy,

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
3 đang được sử dụng trong một chức năng được trang trí bằng @asyncio.coroutine. Nếu bạn thử và sử dụng
import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
3 bên ngoài chức năng này, thì bạn sẽ gặp lỗi từ Python như thế này

  File "main.py", line 1
    file_content = yield from load_file['/Users/scott/data.txt']
                  ^
SyntaxError: 'yield' outside function

Để sử dụng cú pháp này, nó phải nằm trong một hàm khác [thường là với trình trang trí coroutine]

Không đồng bộ/chờ đợi

Cú pháp mới hơn và rõ ràng hơn là sử dụng các từ khóa

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
4. Được giới thiệu trong Python 3. 5, async được sử dụng để khai báo một chức năng như một coroutine, giống như những gì mà trình trang trí @asyncio.coroutine làm. Nó có thể được áp dụng cho hàm bằng cách đặt nó ở phía trước của định nghĩa

Để thực sự gọi hàm này, chúng tôi sử dụng

  File "main.py", line 1
    file_content = yield from load_file['/Users/scott/data.txt']
                  ^
SyntaxError: 'yield' outside function
2, thay vì
import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
3, nhưng theo cách tương tự

async def ping_local[]:
    return await ping_server['192.168.1.1']

Một lần nữa, giống như

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
3, bạn không thể sử dụng this bên ngoài một coroutine khác, nếu không bạn sẽ gặp lỗi cú pháp

Trong Trăn 3. 5, cả hai cách gọi coroutine đều được hỗ trợ, nhưng cách

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
4 được coi là cú pháp chính

Chạy vòng lặp sự kiện

Không có nội dung coroutine nào tôi mô tả ở trên sẽ quan trọng [hoặc hoạt động] nếu bạn không biết cách bắt đầu và chạy một vòng lặp sự kiện. Vòng lặp sự kiện là điểm thực thi trung tâm cho các chức năng không đồng bộ, vì vậy khi bạn thực sự muốn thực thi coroutine, đây là thứ bạn sẽ sử dụng

Vòng lặp sự kiện cung cấp khá nhiều tính năng cho bạn

  • Đăng ký, thực hiện và hủy cuộc gọi bị trì hoãn [chức năng không đồng bộ]
  • Tạo vận chuyển máy khách và máy chủ để liên lạc
  • Tạo các quy trình con và vận chuyển để liên lạc với chương trình khác
  • Chức năng ủy quyền gọi đến một nhóm chủ đề

Hãy xem hướng dẫn thực hành, thực tế của chúng tôi để học Git, với các phương pháp hay nhất, tiêu chuẩn được ngành chấp nhận và bao gồm bảng gian lận. Dừng các lệnh Git trên Google và thực sự tìm hiểu nó

Mặc dù thực tế có khá nhiều cấu hình và loại vòng lặp sự kiện mà bạn có thể sử dụng, nhưng hầu hết các chương trình bạn viết sẽ chỉ cần sử dụng thứ gì đó như thế này để lên lịch cho một chức năng

import asyncio

async def speak_async[]:
    print['OMG asynchronicity!']

loop = asyncio.get_event_loop[]
loop.run_until_complete[speak_async[]]
loop.close[]

Ba dòng cuối cùng là những gì chúng tôi quan tâm ở đây. Nó bắt đầu bằng cách nhận vòng lặp sự kiện mặc định [

  File "main.py", line 1
    file_content = yield from load_file['/Users/scott/data.txt']
                  ^
SyntaxError: 'yield' outside function
6], lên lịch và chạy tác vụ không đồng bộ, sau đó đóng vòng lặp khi chạy xong vòng lặp

Hàm

  File "main.py", line 1
    file_content = yield from load_file['/Users/scott/data.txt']
                  ^
SyntaxError: 'yield' outside function
7 thực sự đang chặn, vì vậy nó sẽ không trả về cho đến khi tất cả các phương thức không đồng bộ được thực hiện. Vì chúng tôi chỉ chạy cái này trên một luồng, nên không có cách nào nó có thể di chuyển về phía trước trong khi vòng lặp đang diễn ra

Bây giờ, bạn có thể nghĩ rằng điều này không hữu ích lắm vì dù sao chúng ta cũng chặn vòng lặp sự kiện [thay vì chỉ các lệnh gọi IO], nhưng hãy tưởng tượng gói toàn bộ chương trình của bạn trong một hàm không đồng bộ, sau đó sẽ cho phép bạn chạy nhiều lệnh không đồng bộ

Bạn thậm chí có thể ngắt vòng lặp sự kiện thành luồng riêng của nó, cho phép nó xử lý tất cả các yêu cầu IO dài trong khi luồng chính xử lý logic chương trình hoặc giao diện người dùng

Một ví dụ

Được rồi, vậy hãy xem một ví dụ lớn hơn một chút mà chúng ta thực sự có thể chạy. Đoạn mã sau là một chương trình không đồng bộ khá đơn giản, lấy JSON từ Reddit, phân tích cú pháp JSON và in ra các bài đăng hàng đầu trong ngày từ /r/python, /r/programming và /r/compsci

Phương thức đầu tiên được hiển thị,

  File "main.py", line 1
    file_content = yield from load_file['/Users/scott/data.txt']
                  ^
SyntaxError: 'yield' outside function
8, được gọi bởi
  File "main.py", line 1
    file_content = yield from load_file['/Users/scott/data.txt']
                  ^
SyntaxError: 'yield' outside function
9 và chỉ tạo một yêu cầu HTTP GET tới URL Reddit thích hợp. Khi điều này được gọi với
  File "main.py", line 1
    file_content = yield from load_file['/Users/scott/data.txt']
                  ^
SyntaxError: 'yield' outside function
2, thì vòng lặp sự kiện có thể tiếp tục và phục vụ các coroutine khác trong khi chờ phản hồi HTTP quay lại. Sau khi thực hiện xong, JSON được trả về
  File "main.py", line 1
    file_content = yield from load_file['/Users/scott/data.txt']
                  ^
SyntaxError: 'yield' outside function
9, được phân tích cú pháp và được in ra

import signal
import sys
import asyncio
import aiohttp
import json

loop = asyncio.get_event_loop[]
client = aiohttp.ClientSession[loop=loop]

async def get_json[client, url]:
    async with client.get[url] as response:
        assert response.status == 200
        return await response.read[]

async def get_reddit_top[subreddit, client]:
    data1 = await get_json[client, '//www.reddit.com/r/' + subreddit + '/top.json?sort=top&t=day&limit=5']

    j = json.loads[data1.decode['utf-8']]
    for i in j['data']['children']:
        score = i['data']['score']
        title = i['data']['title']
        link = i['data']['url']
        print[str[score] + ': ' + title + ' [' + link + ']']

    print['DONE:', subreddit + '\n']

def signal_handler[signal, frame]:
    loop.stop[]
    client.close[]
    sys.exit[0]

signal.signal[signal.SIGINT, signal_handler]

asyncio.ensure_future[get_reddit_top['python', client]]
asyncio.ensure_future[get_reddit_top['programming', client]]
asyncio.ensure_future[get_reddit_top['compsci', client]]
loop.run_forever[]

Mã này hơi khác so với mã mẫu mà chúng tôi đã trình bày trước đó. Để có nhiều coroutine chạy trên vòng lặp sự kiện, chúng tôi đang sử dụng

async def ping_local[]:
    return await ping_server['192.168.1.1']
2 và sau đó chạy vòng lặp mãi mãi để xử lý mọi thứ

Để chạy cái này, trước tiên bạn cần cài đặt

async def ping_local[]:
    return await ping_server['192.168.1.1']
3, bạn có thể làm điều này với PIP

$ pip install aiohttp

Bây giờ chỉ cần chắc chắn rằng bạn chạy nó với Python 3. 5 trở lên, và bạn sẽ nhận được kết quả như thế này

$ python main.py
46: Python async/await Tutorial [//stackabuse.com/python-async-await-tutorial/]
16: Using game theory [and Python] to explain the dilemma of exchanging gifts. Turns out: giving a gift probably feels better than receiving one.. [//vknight.org/unpeudemath/code/2015/12/15/The-Prisoners-Dilemma-of-Christmas-Gifts/]
56: Which version of Python do you use? [This is a poll to compare the popularity of Python 2 vs. Python 3] [//strawpoll.me/6299023]
DONE: python

71: The Semantics of Version Control - Wouter Swierstra [//www.staff.science.uu.nl/~swier004/Talks/vc-semantics-15.pdf]
25: Favorite non-textbook CS books [//www.reddit.com/r/compsci/comments/3xag9e/favorite_nontextbook_cs_books/]
13: CompSci Weekend SuperThread [December 18, 2015] [//www.reddit.com/r/compsci/comments/3xacch/compsci_weekend_superthread_december_18_2015/]
DONE: compsci

1752: 684.8 TB of data is up for grabs due to publicly exposed MongoDB databases [//blog.shodan.io/its-still-the-data-stupid/]
773: Instagram's Million Dollar Bug? [//exfiltrated.com/research-Instagram-RCE.php]
387: Amazingly simple explanation of Diffie-Hellman. His channel has tons of amazing videos and only a few views :[ thought I would share! [//www.youtube.com/watch?v=Afyqwc96M1Y]
DONE: programming

Lưu ý rằng nếu bạn chạy điều này một vài lần, thứ tự in dữ liệu subreddit sẽ thay đổi. Điều này là do mỗi lệnh gọi chúng tôi thực hiện kiểm soát [lợi nhuận] của luồng, cho phép một lệnh gọi HTTP khác xử lý. Cái nào về trước in ra trước

Phần kết luận

Mặc dù chức năng không đồng bộ tích hợp sẵn của Python không mượt mà như JavaScript, nhưng điều đó không có nghĩa là bạn không thể sử dụng nó cho các ứng dụng thú vị và hiệu quả. Chỉ cần dành 30 phút để tìm hiểu thông tin chi tiết và bạn sẽ hiểu rõ hơn nhiều về cách bạn có thể tích hợp điều này vào các ứng dụng của riêng mình

Bạn nghĩ gì về async/await của Python?

Làm cách nào để gọi Python chính không đồng bộ?

Trong asyncio Coroutine có thể được tạo bằng cách sử dụng từ khóa async trước def. Để chạy chức năng không đồng bộ [coroutine], bạn phải gọi nó bằng Vòng lặp sự kiện . Vòng lặp sự kiện. Bạn có thể coi Vòng lặp sự kiện là các chức năng để chạy các tác vụ và lệnh gọi lại không đồng bộ, thực hiện các hoạt động IO mạng và chạy các quy trình con.

Bạn có thể gọi một chức năng không đồng bộ không?

Các khai báo hàm không đồng bộ được nâng lên đầu phạm vi của chúng và có thể được gọi ở bất kỳ đâu trong phạm vi của chúng .

Bạn có thể gọi một hàm async mà không cần chờ Python không?

Không thể , vì kết quả đó không tồn tại. Gọi hàm async không tạo chuỗi, nó tạo một máy trạng thái sẽ tạo chuỗi khi được thăm dò. . await đang thực sự chạy mã.

Python có thể chạy 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ộ 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 tác vụ phân tán, v.v. asyncio thường phù hợp hoàn hảo với mã mạng có cấu trúc cấp cao và ràng buộc IO.

Chủ Đề