9.1 – Coroutine Basics


Lua cung cấp tất cả các hàm coroutine của nó được đóng gói trong bảng coroutine. Hàm tạo sẽ tạo ra các coroutines mới. Nó có một đối số duy nhất, một hàm với mã mà chương trình đăng quang sẽ chạy. Nó trả về một giá trị kiểu chuỗi, đại diện cho quy trình đăng quang mới. Thông thường, đối số để tạo là một hàm ẩn danh, như sau:

co = coroutine.create(function ()
           print("hi")
         end)
    
    print(co)   --> thread: 0x8071d98

Quy trình đăng quang có thể ở một trong ba trạng thái khác nhau: tạm ngừng, đang chạy và chết. Khi chúng tôi tạo một quy trình đăng ký, nó sẽ bắt đầu ở trạng thái bị treo. Điều đó có nghĩa là một chương trình đăng quang không tự động chạy phần thân của nó khi chúng ta tạo nó. Chúng ta có thể kiểm tra trạng thái của một quy trình bằng hàm trạng thái:

print(coroutine.status(co))   --> suspended Bị treo

Hàm coroutine.resume (re) bắt đầu thực hiện một coroutine, thay đổi trạng thái của nó từ bị treo sang đang chạy:

coroutine.resume(co)   --> hi

Trong ví dụ này, phần thân của chương trình đăng quang chỉ đơn giản là in “hi” và kết thúc, để lại chương trình đăng quang ở trạng thái chết, từ đó nó không thể quay trở lại:

print(coroutine.status(co))   --> dead

Cho đến nay, coroutines trông không khác gì một cách phức tạp để gọi các hàm. Sức mạnh thực sự của coroutines bắt nguồn từ hàm sản lượng, cho phép một hàm coroutine đang chạy tạm dừng việc thực thi của nó để nó có thể được tiếp tục lại sau này. Hãy để chúng tôi xem một ví dụ đơn giản:

co = coroutine.create(function ()
           for i=1,10 do
             print("co", i)
             coroutine.yield()
           end
         end)

Bây giờ, khi chúng ta tiếp tục quy trình đăng ký này, nó sẽ bắt đầu thực thi và chạy cho đến khi kết quả đầu tiên:

coroutine.resume(co)    --> co   1

Nếu chúng tôi kiểm tra trạng thái của nó, chúng tôi có thể thấy rằng quy trình đăng ký bị tạm ngừng và do đó có thể được tiếp tục lại:

 print(coroutine.status(co))   --> suspended

Theo quan điểm của chương trình điều tra, tất cả các hoạt động xảy ra trong khi nó bị tạm ngừng đều diễn ra bên trong lời kêu gọi nhường nhịn. Khi chúng ta tiếp tục quy trình đăng ký, lệnh gọi kết quả này cuối cùng cũng trở lại và quy trình đăng ký tiếp tục thực hiện cho đến khi kết quả tiếp theo hoặc cho đến khi kết thúc:

    coroutine.resume(co)    --> co   2
    coroutine.resume(co)    --> co   3
    ...
    coroutine.resume(co)    --> co   10
    coroutine.resume(co)    -- prints nothing

Trong lần gọi cuối cùng để tiếp tục, phần thân điều tra đã hoàn thành vòng lặp và sau đó quay trở lại, do đó, chương trình điều tra hiện đã chết. Nếu chúng tôi cố gắng tiếp tục lại, tiếp tục trả về false cộng với thông báo lỗi:

print(coroutine.resume(co))
    --> false   cannot resume dead coroutine

Lưu ý rằng tiếp tục chạy ở chế độ được bảo vệ. Do đó, nếu có bất kỳ lỗi nào bên trong một chương trình điều tra, Lua sẽ không hiển thị thông báo lỗi, mà thay vào đó, nó sẽ đưa nó trở lại cuộc gọi tiếp tục.

Một cơ sở hữu ích trong Lua là lợi suất sơ yếu lý lịch theo cặp có thể trao đổi dữ liệu giữa chúng. Sơ yếu lý lịch đầu tiên, không có lợi nhuận tương ứng đang chờ đợi nó, chuyển các đối số bổ sung của nó làm đối số cho hàm chính của chương trình điều tra:

co = coroutine.create(function (a,b,c)
           print("co", a,b,c)
         end)
    coroutine.resume(co, 1, 2, 3)    --> co  1  2  3

Một lệnh gọi để tiếp tục trả về, sau giá trị true báo hiệu không có lỗi, bất kỳ đối số nào được chuyển đến lợi suất tương ứng:

 co = coroutine.create(function (a,b)
           coroutine.yield(a + b, a - b)
         end)
    print(coroutine.resume(co, 20, 10))  --> true  30  10

Đối xứng, lợi nhuận trả về bất kỳ đối số bổ sung nào được chuyển đến sơ yếu lý lịch tương ứng:

co = coroutine.create (function ()
           print("co", coroutine.yield())
         end)
    coroutine.resume(co)
    coroutine.resume(co, 4, 5)     --> co  4  5

Cuối cùng, khi một quy trình đăng quang kết thúc, bất kỳ giá trị nào được trả về bởi hàm chính của nó sẽ được chuyển đến sơ yếu lý lịch tương ứng:

co = coroutine.create(function ()
           return 6, 7
         end)
    print(coroutine.resume(co))   --> true  6  7

Chúng tôi hiếm khi sử dụng tất cả các phương tiện này trong cùng một quy trình, nhưng tất cả chúng đều có mục đích sử dụng.

Đối với những người đã biết điều gì đó về coroutines, điều quan trọng là phải làm rõ một số khái niệm trước khi chúng ta tiếp tục. Lua cung cấp những gì tôi gọi là coroutines không đối xứng. Điều đó có nghĩa là nó có một chức năng để tạm ngừng thực hiện một quy trình điều tra và một chức năng khác để tiếp tục một quy trình điều tra bị tạm ngừng. Một số ngôn ngữ khác cung cấp các quy trình đối xứng, trong đó chỉ có một chức năng để chuyển quyền điều khiển từ bất kỳ quy trình đăng ký nào sang quy trình khác.

Một số người gọi là chu kỳ bán nguyệt không đối xứng (bởi vì chúng không đối xứng, chúng không thực sự là đồng). Tuy nhiên, những người khác sử dụng thuật ngữ bán đăng ký tương tự để biểu thị việc triển khai hạn chế các coroutines, trong đó một quy trình chỉ có thể tạm ngừng thực thi khi nó không ở bên trong bất kỳ chức năng phụ trợ nào, tức là khi nó không có lệnh gọi đang chờ xử lý nào trong ngăn xếp điều khiển của nó. Nói cách khác, chỉ có phần thân chính của các bán phần tử như vậy mới có thể mang lại hiệu quả. Một trình tạo trong Python là một ví dụ về ý nghĩa này của các semi-coroutines.

Không giống như sự khác biệt giữa corout đối xứng và bất đối xứng, sự khác biệt giữa coroutines và các trình tạo (như được trình bày trong Python) là một điểm sâu sắc; máy phát điện đơn giản là không đủ mạnh để thực hiện một số cấu trúc thú vị mà chúng ta có thể viết bằng các coroutines thực sự. Lua cung cấp các quy trình thực tế, không đối xứng. Những người thích các coroutines đối xứng có thể thực hiện chúng trên đầu các cơ sở không đối xứng của Lua. Đó là một nhiệm vụ dễ dàng. (Về cơ bản, mỗi lần chuyển nhượng sẽ mang lại lợi nhuận theo sau là một bản lý lịch.)

Leave a comment