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
import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
9 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 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ạiimport 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, nhưng 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 thi sau này
Trong trường hợp bạn cần xác định xem một hàm 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 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']
1 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 đối tượng được trả về từ hàm có phải là đối tượng coroutine hay không, 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']
2 để 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 [mà 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ư sauimport 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 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']
3 bên ngoài chức năng này, thì bạn sẽ gặp lỗi từ Python như thế nàyimport asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
0Để 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, import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
9 đượ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í import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
0 thực hiện. 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
import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
02, 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ựimport asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
4Mộ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 cái này bên ngoài một coroutine khác, nếu không bạn sẽ gặp lỗi cú phápTrong Python 3. 5, cả hai cách gọi coroutine đều được hỗ trợ, nhưng cách gọi
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ínhChạ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
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
7Ba 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 [
import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
06], 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ặpHàm
import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
07 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 raBây giờ, bạn có thể nghĩ rằng điều này không hữu ích lắm vì cuối cùng 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ị,
import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
08, được gọi bởi import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
09 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 import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
02, vòng lặp sự kiện sau đó 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ề import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
09, được phân tích cú pháp và được in raimport asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
4Mã 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
import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
42 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
import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
43, bạn có thể làm điều này với PIPimport asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
7Bâ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
import asyncio
@asyncio.coroutine
def get_json[client, url]:
file_content = yield from load_file['/Users/scott/data.txt']
8Lư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?