Python - Coroutines

Python Coroutines là một khái niệm cơ bản trong lập trình, mở rộng khả năng của các hàm truyền thống. Chúng đặc biệt hữu ích cho lập trình bất đồng bộ và các quy trình xử lý dữ liệu phức tạp.

Coroutines là một phần mở rộng của khái niệm hàm và bộ sinh. Chúng được thiết kế để thực hiện đa nhiệm hợp tác và quản lý các hoạt động bất đồng bộ.

Trong các hàm truyền thống, tức là các thủ tục con, chúng có một điểm vào và một điểm ra duy nhất, trong khi đó, các coroutine có thể tạm dừng và tiếp tục thực thi tại nhiều điểm khác nhau, làm cho chúng trở nên linh hoạt hơn.

Key Characteristics of Coroutines

Dưới đây là những đặc điểm chính của Coroutines trong Python:

  • Multiple Entry Points: Coroutines are not constrained to a single entry point like traditional functions. They can pause their execution at certain points, when they hit a yield statement and resume later. This allows coroutines to handle complex workflows that involve waiting for or processing asynchronous data.
  • No Central Coordinator: When we see traditional functions i.e subroutines, which are often coordinated by a main function but coroutines operate more independently. They can interact with each other in a pipeline fashion where data flows through a series of coroutines, each performing a different task.
  • Cooperative Multitasking: Coroutines enable cooperative multitasking. This means that instead of relying on the operating system or runtime to switch between tasks the programmer controls when coroutines yield and resume by allowing for more fine-grained control over execution flow.

Subroutines Vs. Coroutines

Subroutines là các hàm truyền thống với một điểm vào duy nhất và không có cơ chế nội tại để tạm dừng hoặc tiếp tục thực thi. Chúng được gọi theo một trình tự xác định và xử lý các tác vụ với luồng điều khiển đơn giản.

Coroutines là các chức năng nâng cao với nhiều điểm vào có thể tạm dừng và tiếp tục thực thi của chúng. Chúng hữu ích cho các tác vụ yêu cầu thực thi không đồng bộ, luồng điều khiển phức tạp và đường ống dữ liệu. Chúng hỗ trợ đa nhiệm hợp tác bằng cách cho phép lập trình viên kiểm soát khi nào việc thực thi chuyển đổi giữa các tác vụ.

Bảng dưới đây giúp hiểu rõ những khác biệt và điểm tương đồng chính giữa subroutine và coroutine bằng cách giúp chúng ta dễ dàng nắm bắt vai trò và chức năng của chúng trong lập trình.

Criteria Subroutines Coroutines
Definition A sequence of instructions performing a task. A generalization of subroutines that can pause and resume execution.
Entry Points Single entry point. Multiple entry points; can pause and resume execution.
Execution Control Called by a main function or control structure. These can suspend execution and be resumed later and programmer controls switching.
Purpose Perform a specific task or computation. Manage asynchronous operations, cooperative multitasking and complex workflows.
Calling Mechanism Typically called by a main function or other subroutines. Invoked and controlled using 'next()', 'send()', and 'close()' methods.
Data Handling No built-in mechanism for handling data exchanges; typically uses parameters and return values. Can receive and process data using 'yield' with 'send()'.
State Management No inherent mechanism to maintain state between calls. Maintains execution state between suspensions and can resume from where it left off.
Usage These are used for modularizing code into manageable chunks. These are used for asynchronous programming, managing data pipelines and cooperative multitasking.
Concurrency Not inherently designed for concurrent execution; typically used in sequential programming. Supports cooperative multitasking and can work with asynchronous tasks.
Example Usage Helper functions, utility functions. Data pipelines, asynchronous tasks, cooperative multitasking.
Control Flow Execution follows a linear path through the code. Execution can jump back and forth between coroutines based on yield points.

Execution of Coroutines

Coroutines được khởi tạo với phương thức __next__() mà bắt đầu coroutine và tiến hành thực thi đến câu lệnh yield đầu tiên. Coroutine sau đó chờ đợi một giá trị được gửi đến nó. Phương thức send() được sử dụng để gửi giá trị đến coroutine, mà sau đó có thể xử lý các giá trị này và có khả năng trả về kết quả.

Example of Basic Coroutine

Một coroutine sử dụng câu lệnh yield, có thể gửi và nhận giá trị. Khác với một generator mà trả về giá trị để lặp lại, coroutine thường sử dụng yield để nhận đầu vào và thực hiện các hành động dựa trên đầu vào đó. Dưới đây là ví dụ cơ bản về coroutine trong Python −

def print_name(prefix):
    print(f"Searching prefix: {prefix}")
    while True:
        name = (yield)
        if prefix in name:
            print(name)

# Instantiate the coroutine
corou = print_name("Welcome to")

# Start the coroutine
corou.__next__()

# Send values to the coroutine
corou.send("Tutorialspoint")
corou.send("Welcome to Tutorialspoint")

Output

Searching prefix: Welcome to
Welcome to Tutorialspoint

Closing a Coroutine

Coroutines có thể chạy vô thời hạn, vì vậy việc đóng chúng đúng cách khi không còn cần thiết là rất quan trọng. Phương thức close() kết thúc coroutine và xử lý việc dọn dẹp. Nếu chúng ta cố gắng gửi dữ liệu đến một coroutine đã đóng, nó sẽ gây ra một StopIteration exception. .

Example

Dưới đây là ví dụ về việc đóng một coroutine trong Python −

def print_name(prefix):
    print(f"Searching prefix: {prefix}")
    try:
        while True:
            name = (yield)
            if prefix in name:
                print(name)
    except GeneratorExit:
        print("Closing coroutine!!")

# Instantiate and start the coroutine
corou = print_name("Come")
corou.__next__()

# Send values to the coroutine
corou.send("Come back Thank You")
corou.send("Thank you")

# Close the coroutine
corou.close()

Output

Searching prefix: Come
Come back Thank You
Closing coroutine!!

Chaining Coroutines for Pipelines

Coroutines có thể được nối lại với nhau để tạo thành một pipeline xử lý, cho phép dữ liệu chảy qua một loạt các giai đoạn. Điều này đặc biệt hữu ích cho việc xử lý các chuỗi dữ liệu theo từng giai đoạn, trong đó mỗi giai đoạn thực hiện một nhiệm vụ cụ thể.

Example

Dưới đây là ví dụ cho thấy việc kết nối các coroutine để tạo thành pipeline −

def producer(sentence, next_coroutine):
   '''
   Splits the input sentence into tokens and sends them to the next coroutine.
   '''
   tokens = sentence.split(" ")
   for token in tokens:
      next_coroutine.send(token)
   next_coroutine.close()

def pattern_filter(pattern="ing", next_coroutine=None):
   '''
   Filters tokens based on the specified pattern and sends matching tokens to the next coroutine.
   '''
   print(f"Searching for {pattern}")
   try:
      while True:
         token = (yield)
         if pattern in token:
            next_coroutine.send(token)
   except GeneratorExit:
      print("Done with filtering!!")
      next_coroutine.close()

def print_token():
   '''
   Receives tokens and prints them.
   '''
   print("I'm the sink, I'll print tokens")
   try:
      while True:
         token = (yield)
         print(token)
   except GeneratorExit:
      print("Done with printing!")

# Setting up the pipeline
pt = print_token()
pt.__next__()

pf = pattern_filter(next_coroutine=pt)
pf.__next__()

sentence = "Tutorialspoint is welcoming you to learn and succeed in Career!!!"
producer(sentence, pf)

Output

I'm the sink, I'll print tokens
Searching for ing
welcoming
Done with filtering!!
Done with printing!