Hướng dẫn python await async function - python đang chờ hàm không đồng bộ

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

Ví dụ: thay vì chờ yêu cầu HTTP hoàn thành trước khi tiếp tục thực hiện, với Python Async Coroutines, 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ờ đợi trong hàng đợi trong khi chờ yêu cầu HTTP hoàn thành. Có thể mất nhiều suy nghĩ hơn một chút để có được logic đúng, 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ả sau đó, cú pháp và thực hiện các hàm không đồng bộ trong các ngôn ngữ như Python thực sự không khó. Bây giờ, JavaScript là một câu chuyện khác, nhưng Python dường như thực hiện nó khá tốt.

Sự 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ứ điều gì từ một cuộc gọi cơ sở dữ liệu từ xa để đăng lên dịch vụ nghỉ ngơi. 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 đang 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 bạn xử lý các tác vụ khác trong khi chờ các tài nguyên khác trả lời.

Coroutines

Một chức năng không đồng bộ trong Python thường được gọi là 'Coroutine', chỉ là một hàm sử dụng từ khóa async hoặc một hàm được trang trí bằng

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
0. Một trong hai chức năng dưới đây sẽ hoạt động như một coroutine và tương đương với loại:

import asyncio

async def ping_server[ip]:
    pass

@asyncio.coroutine
def load_file[path]:
    pass

Đây là các chức năng đặc biệt trả về các đối tượng Coroutine khi được gọi. Nếu bạn quen thuộc với những lời hứa của JavaScript, thì bạn có thể nghĩ về đối tượng được trả lại này gần giống như một lời hứa. 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 Coroutine được trả về, sau đó có thể được chuyển đến vòng lặp sự kiện để được thực hiện sau này.

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

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
1 cung cấp phương thức
import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
2 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ừ hàm là đối tượng coroutine, bạn có thể sử dụng
import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
3 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 pháp

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
4. Đ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ưới dạng
import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
5 [mà chúng ta sẽ đến sau].

Biểu thức

import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
4 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']
4 đang được sử dụng trong một chức năng được trang trí bằng
import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
0. 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']
4 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 chức năng khác [thường là với bộ trang trí coroutine].

Async/await

Cú pháp mới hơn và sạch 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']
5. Được giới thiệu trong Python 3.5, async được sử dụng để khai báo một chức năng là một coroutine, giống như những gì người trang trí
import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
0 làm. Nó có thể được áp dụng cho chức năng bằng cách đặt nó ở phía trước của định nghĩa:

async def ping_server[ip]:
    # ping code here...

Để thực sự gọi chức năng 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
3, thay vì
import asyncio

@asyncio.coroutine
def get_json[client, url]:
    file_content = yield from load_file['/Users/scott/data.txt']
4, 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']
4, bạn không thể sử dụng điều này bên ngoài một coroutine khác, nếu không bạn sẽ gặp lỗi cú pháp.

Trong Python 3.5, cả hai cách gọi coroutines đề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']
5 có nghĩa là cú pháp chính.

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

Không có thứ nào trong số những thứ coroutine tôi mô tả ở trên sẽ quan trọng [hoặc làm việc] 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 muốn thực sự thực hiện coroutine, đây là những gì 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 thi và hủy các 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 phụ và vận chuyển để liên lạc với một chương trình khác
  • Các cuộc gọi chức năng ủy quyền đến một nhóm các luồng

Kiểm tra hướng dẫn thực hành của chúng tôi, thực tế để học Git, với các thực hành tốt nhất, các tiêu chuẩn được công nghiệp chấp nhận và bao gồm bảng gian lận. Ngừng các lệnh git googling và thực sự tìm hiểu nó!

Mặc dù thực sự có khá nhiều cấu hình và các loại vòng lặp sự kiện bạn có thể sử dụng, hầu hết các chương trình bạn viết sẽ chỉ cần sử dụng một cái gì đó như thế này để lên lịch 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 lấy 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
7], lập lịch và chạy tác vụ Async, sau đó đóng vòng lặp khi vòng lặp được thực hiện.

Hàm

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

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

Bạn thậm chí có thể chia vòng lặp sự kiện vào chủ đề của riêng nó, để 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 UI.

Một ví dụ

Được rồi, vì vậy chúng ta 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. Mã sau đây là một chương trình không đồng bộ khá đơn giản, lấy JSON từ Reddit, phân tích lại JSON và in ra các bài đăng hàng đầu trong ngày từ /r /python, /r /lập trình và /r /compsci.

Phương pháp đầ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
9, được gọi bởi
async def ping_server[ip]:
    # ping code here...
0 và chỉ tạo một HTTP nhận yêu cầu đến 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
3, vòng lặp sự kiện sau đó có thể tiếp tục và phục vụ các coroutines khác trong khi chờ phản hồi HTTP để quay lại. Khi đã xảy ra, JSON được trả lại cho
async def ping_server[ip]:
    # ping code here...
0, đượ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[]

Điều này hơi khác so với mã mẫu mà chúng tôi đã hiển thị trước đó. Để có được nhiều coroutines chạy trên vòng lặp sự kiện, chúng tôi sẽ sử dụng

async def ping_server[ip]:
    # ping code here...
3 và sau đó chạy vòng lặp mãi mãi để xử lý mọi thứ.

Để chạy điều này, bạn sẽ cần cài đặt

async def ping_server[ip]:
    # ping code here...
4 trước, điều mà bạn có thể làm với PIP:

$ pip install aiohttp

Bây giờ chỉ cần đảm bảo rằng bạn chạy nó với Python 3.5 trở lên, và bạn sẽ nhận được một đầu ra 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ự dữ liệu subreddit được in ra thay đổi. Điều này là do mỗi cuộc gọi chúng tôi thực hiện các bản phát hành [sản lượng] của luồng, cho phép một cuộc gọi HTTP khác xử lý. Bất cứ điều gì một người trở lại đầu tiên được in ra trước.

Sự kết luận

Mặc dù chức năng không đồng bộ tích hợp của Python không hoàn toàn mượt mà như của 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 mất 30 phút để tìm hiểu về nó và bạn sẽ có ý thức tốt 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 bạn.

Bạn nghĩ gì về Async/chờ đợi của Python? Bạn đã sử dụng nó như thế nào trong quá khứ? Hãy cho chúng tôi biết trong các ý kiến!

Bài Viết Liên Quan

Chủ Đề