Thư Viện Đa Luồng Trong Lua Nhúng - nói dối e blog

Thư Viện Đa Luồng Trong Lua Nhúng

Khi sử dụng Lua như một ngôn ngữ nhúng trên cả phía máy khách lẫn máy chủ, người ta thường mong muốn tận dụng cơ chế đa luồng cho mã kịch bản Lua. Điều này đặc biệt cần thiết khi logic nghiệp vụ của bạn bao gồm nhiều luồng xử lý độc lập nhưng lại muốn chúng chạy chung trong cùng một máy ảo Lua.

Lua cung cấp sẵn coroutine - một công cụ mô phỏng đa luồng cực kỳ hiệu quả. Trên thực tế, chính Lua cũng gọi kiểu dữ liệu coroutine là “thread”. Trong quá trình xem xét lại cách đóng gói Lua trong Skynet gần đây, tôi nhận thấy một hạn chế quan trọng: luồng chính không được phép gọi các API chặn (blocking API). Nói cách khác, không thể yield trực tiếp trong mã xử lý chính. Điều này khiến tôi suy nghĩ về một phương pháp đóng gói tối ưu hơn, không chỉ giải quyết được hạn chế này mà còn đơn giản hóa nhiều phần mã nguồn liên quan.

Dưới đây là ý tưởng mới của tôi, có thể áp dụng không chỉ cho Skynet mà còn cho mọi ứng dụng sử dụng Lua như ngôn ngữ nhúng (những ứng dụng do bạn tự xây dựng phần mã host, ví dụ như ứng dụng máy khách):

Giải pháp kiến trúc mới:

  1. Ngay khi tải module đầu tiên (trong Skynet là lệnh require "skynet"), hãy khởi tạo một coroutine chuyên dụng đóng vai trò bộ lập lịch (scheduler).
  2. Coroutine này quản lý một mảng chứa toàn bộ các luồng xử lý nghiệp vụ.
  3. Con trỏ lua_State tương ứng với coroutine lập lịch cần được lưu trữ trong bảng đăng ký (registry) của Lua VM, đảm bảo tồn tại suốt vòng đời của máy ảo. Điều này cho phép phần mềm host lưu giữ an toàn đối tượng này.

Cải tiến trong phần mềm host:

  • Ngoài con trỏ L truyền thống đại diện cho VM và luồng chính, chúng ta thêm con trỏ cL mới đại diện cho coroutine lập lịch.
  • Khi khởi động logic chính, sau khi tạo VM, thay vì chạy trực tiếp luồng chính bằng lua_resume, ta thực hiện:
    • Nếu luồng chính kết thúc bình thường → không dùng tiếp con trỏ L nữa
    • Nếu luồng chính bị tạm dừng → đưa nó vào danh sách quản lý của bộ lập lịch, từ đó được xử lý như một luồng nghiệp vụ thông thường

Cơ chế lập lịch thông minh:

  • Cung cấp đồng thời API Lua và C để thêm các coroutine mới vào mảng quản lý.
  • Bộ lập lịch đảm nhiệm nhiệm vụ:
    • Liên tục kiểm tra trạng thái rỗi để tiếp tục thực thi (resume) các coroutine mới
    • Đánh thức các coroutine bị treo khi có tin nhắn IO ngoại vi đến
  • Trong quá trình lập lịch, luồng chính và các luồng mới tạo không có sự phân biệt đặc biệt.

Ưu điểm vượt trội:

  • Thuật toán lập lịch không cần phức tạp, hoàn toàn có thể triển khai bằng Lua thuần túy mà không cần dùng đến API của phần mềm host.
  • Với cơ chế này, có thể cải tiến thư viện coroutine gốc của Lua để đảm bảo sự tương tác trơn tru giữa các coroutine nghiệp vụ và thư viện đa luồng dựa trên coroutine.

Giải pháp này mở ra một hướng tiếp cận mới cho các hệ thống nhúng Lua, giúp tối ưu hóa hiệu năng xử lý đồng thời giữ được tính đơn giản trong thiết kế hệ thống. Tôi đã trình bày chi tiết hơn về việc tích hợp với thư viện coroutine gốc trong một bài viết trước đây.

0%