CodeGym /Khóa học Java /Python SELF VI /Các phương thức bất đồng bộ

Các phương thức bất đồng bộ

Python SELF VI
Mức độ , Bài học
Có sẵn

3.1 Mô-đun asyncio

Chẳng ai tạo ra luồng riêng cho các tác vụ bất đồng bộ nữa. Thực ra, thì có thể tạo, nhưng như vậy được cho là quá thấp cấp và chỉ được dùng bởi các lập trình viên framework. Và chỉ khi không thể thiếu chúng.

Giờ đây, lập trình bất đồng bộ, các toán tử async/awaitcoroutines với tasks là xu hướng. Nhưng hãy từ từ…

Một chút lịch sử

Trước kia, trong Python để giải quyết bài toán lập trình bất đồng bộ, người ta sử dụng coroutines dựa trên generators. Sau đó, trong Python 3.4, mô-đun asyncio (đôi khi được viết là async IO) xuất hiện, trong đó có cơ chế lập trình bất đồng bộ. Trong Python 3.5, cấu trúc async/await đã có mặt.

Và giờ đây một chút thông tin dẫn nhập. Đầu tiên tôi sẽ nói ngắn gọn về những thứ đó, sau đó chi tiết hơn, và sau đó chi tiết hơn nữa. Không còn cách nào khác, vì gần như tất cả chúng đều hoạt động kết hợp, và không thể mô tả chi tiết một thực thể mà không liên quan đến các thực thể khác.

Mô-đun asyncio

Mô-đun asyncio được thiết kế để viết chương trình bất đồng bộ, cung cấp khả năng thực thi các tác vụ song song. Nó hỗ trợ các thao tác I/O bất đồng bộ, timers, sockets, thực thi coroutines và đa luồng, hoạt động trong một hoặc nhiều vòng sự kiện.

Coroutines (Coroutines)

Coroutinescác hàm bất đồng bộ, được định nghĩa bằng từ khóa async def. Coroutines cho phép ngừng thực thi của mình bằng từ khóa await, cho phép các coroutines khác thực thi trong thời gian đó.


import asyncio

# Định nghĩa hàm bất đồng bộ (coroutine)
async def main():
    print('Hello ...')
    # Ngừng thực thi trong 1 giây
    await asyncio.sleep(1)
    print('... World!')

# Khởi động hàm bất đồng bộ main() trong vòng sự kiện
asyncio.run(main())

Vòng sự kiện (Event Loop)

Vòng sự kiện quản lý việc thực thi coroutines, tasks và các thao tác bất đồng bộ khác. Lệnh gọi asyncio.run() bắt đầu vòng sự kiện và thực thi coroutine cho đến khi hoàn thành.


import asyncio

async def main():
    print('Hello ...')
    await asyncio.sleep(1)
    print('... World!')

# Lấy vòng sự kiện hiện tại
loop = asyncio.get_event_loop()
# Thực thi coroutine cho đến khi hoàn thành
loop.run_until_complete(main())
# Đóng vòng sự kiện sau khi hoàn thành tất cả các task
loop.close()

Tasks (Tasks)

Tasks cho phép chạy coroutines song song. Được tạo bằng asyncio.create_task() hoặc asyncio.ensure_future().


import asyncio

# Định nghĩa coroutine, sẽ được thực thi với độ trễ
async def say_after(delay, what):
    # Ngừng thực thi trong thời gian đã định
    await asyncio.sleep(delay)
    print(what)

# Coroutine chính
async def main():
    # Tạo tasks để thực thi song song coroutines
    task1 = asyncio.create_task(say_after(1, 'hello'))
    task2 = asyncio.create_task(say_after(2, 'world'))
    
    # Chờ đợi cả hai tasks hoàn thành
    await task1
    await task2

# Khởi động coroutine chính
asyncio.run(main())

Futures (Futures)

Các đối tượng Future đại diện cho kết quả của các thao tác bất đồng bộ, sẽ có sẵn trong tương lai. Chúng được sử dụng để chờ đợi hoàn thành task bất đồng bộ.


import asyncio

# Định nghĩa coroutine, giả lập task dài
async def long_running_task():
    print('Task started')
    # Ngừng thực thi trong 3 giây
    await asyncio.sleep(3)
    print('Task finished')
    return 'Result'

# Coroutine chính
async def main():
    # Tạo future để chờ đợi task hoàn thành
    future = asyncio.ensure_future(long_running_task())
    # Chờ đợi task hoàn thành và lấy kết quả
    result = await future  
    print(f'Task result: {result}')

# Khởi động coroutine chính
asyncio.run(main())

3.2 Hàm bất đồng bộ — async def

Hàm bất đồng bộ được khai báo giống như hàm bình thường, chỉ cần thêm từ async trước từ khóa def.


async def TênHàm(các_tham_số):
    mã hàm

Hàm bất đồng bộ được khai báo như hàm bình thường, được gọi như hàm bình thường, nhưng kết quả của nó trả về lại khác. Nếu gọi hàm bất đồng bộ, nó sẽ không trả về kết quả, mà trả về một đối tượng đặc biệt — coroutine.

Chúng ta thậm chí có thể kiểm tra điều đó:


import asyncio

async def main():
    print("Hello World")
            
# Gọi hàm bất đồng bộ, trả về coroutine
result = main()
# Kiểm tra kiểu của kết quả
print(type(result)) # <class 'coroutine'>

Điều gì xảy ra? Khi bạn đánh dấu hàm bằng từ async, thì thực sự bạn đã thêm vào nó một decorator, làm điều này:


def async_decorator(func):
    # Tạo đối tượng Task
    task = Task()
    # Truyền vào đó hàm func của chúng ta, để nó thực thi
    task.target = func  
    # Thêm đối tượng task vào hàng đợi tasks — Event Loop
    eventloop.add_task(task)  
    # Trả về đối tượng task
    return task 

Và mã của bạn trông như thế này:


import asyncio

@async_decorator
def main():
    print("Hello World")
            
result = main()
print(type(result)) # <class 'coroutine'>

Ý nghĩa của sự tương tự này là:

Khi bạn gọi hàm bất đồng bộ, một đối tượng đặc biệt Task được tạo ra, sẽ thực thi hàm của bạn, nhưng vào thời điểm nào đó trong tương lai. Có thể sau 0.0001 giây, hoặc có thể 10 giây sau.

Đối tượng task này ngay lập tức được trả về như kết quả của lệnh gọi hàm bất đồng bộ của bạn. Đây chính là coroutine. Hàm bất đồng bộ của bạn có thể chưa bắt đầu thực thi, mà đối tượng task (coroutine) đã có sẵn.

Tại sao bạn cần đến task (coroutine) này? Bạn không thể làm nhiều thứ với nó, nhưng có thể làm 3 điều:

  • Chờ đợi cho đến khi hàm bất đồng bộ hoàn thành.
  • Chờ đợi cho đến khi hàm bất đồng bộ kết thúc và nhận kết quả từ coroutine.
  • Chờ cho đến khi hoàn thành 10 (bất kỳ số nào) hàm bất đồng bộ.

Cách làm điều đó, tôi sẽ nói bên dưới.

3.3 Toán tử await

Phần lớn các hành động với coroutine bắt đầu với "chờ đợi thực thi hàm bất đồng bộ". Do đó, chúng ta có một toán tử await đặc biệt cho hành động này.

Bạn chỉ cần viết nó trước coroutine:


await coroutine

Hoặc ngay trước khi gọi hàm bất đồng bộ:


await hàm_bất_đồng_bộ(các tham số)

Khi Python gặp toán tử await trong mã, nó tạm ngừng thực thi hàm hiện tại và chờ đợi cho đến khi coroutine hoàn tất — cho đến khi kết thúc hàm bất đồng bộ mà coroutine trỏ đến.

Quan trọng! Toán tử await chỉ được sử dụng trong hàm bất đồng bộ để tạm dừng thực thi cho đến khi một coroutine khác hoặc một thao tác bất đồng bộ hoàn tất.

Điều này được thực hiện để đơn giản hóa quá trình chuyển đổi giữa các lệnh gọi hàm bất đồng bộ. Một lệnh gọi await thực chất là sự tuyên bố "chúng tôi sẽ chờ đợi ở đây không biết bao lâu — hãy thực hiện các hàm bất đồng bộ khác".

Ví dụ:


import asyncio

# Định nghĩa hàm bất đồng bộ
async def async_print(text):
    print(text)
        
# Hàm bất đồng bộ chính
async def main():
    # Sử dụng await để chờ đợi thực thi hàm bất đồng bộ
    await async_print("Hello World")
        
# Khởi động vòng sự kiện chính và thực thi coroutine main()
asyncio.run(main()) #khởi động hàm bất đồng bộ

Thực ra, toán tử await hoạt động phức tạp hơn — nó cũng trả về kết quả thực thi của hàm bất đồng bộ mà nó đã được gọi.

Ví dụ:


import asyncio

# Định nghĩa hàm bất đồng bộ, cộng hai số
async def async_add(a, b):
    return a + b
        
# Hàm bất đồng bộ chính
async def main():
    # Sử dụng await để lấy kết quả thực thi async_add
    sum = await async_add(100, 200)
    print(sum)
        
# Khởi động vòng sự kiện chính và thực thi coroutine main()
asyncio.run(main()) #khởi động hàm bất đồng bộ

Vậy nên, tóm lại, toán tử await:

  • Tạm dừng hàm bất đồng bộ hiện tại cho đến khi hoàn tất một coroutine khác hoặc một thao tác bất đồng bộ.
  • Trả về kết quả thực thi của thao tác hay coroutine bất đồng bộ.
  • Có thể sử dụng chỉ trong hàm bất đồng bộ.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION