Asynchronous (async) là một phương thức xử lý và trao đổi thông tin: Nếu một quá trình diễn ra cần sự hoàn thành của một quá trình khác, chỉ mình nó cần phải chờ đợi, các quá trình khác không cần phụ thuộc vẫn sẽ được diễn ra.
Trái ngược với synchronous (sync), mọi thứ phải chờ một công việc hiện tại thực hiện xong thì mới có thể tiếp tục. Ví dụ: chương trình của bạn phải chờ từng hàm một thực thi xong mới có thể gọi một hàm khác.
Mở rộng khái niệm trên áp dụng vào networking, giả sử bạn lập trình một server. Bạn sẽ chọn hướng tiếp cận nào sau đây:
– Sync: mỗi client kết nối đến, server của bạn phải phục vụ client 1 xong mới có thể phục vụ client 2, xong 2 mới sang client 3, … điều gì sẽ xảy ra khi một client phải phục vụ lâu hơn bình thường? Tất cả đều sẽ phải chờ đợi client đó.
– Async: server đang phục vụ client 1, có client 2 kết nối đến, server sẽ phục vụ luôn client 2, bởi client 2 hoàn toàn không phụ thuộc vào client 1, tương tự, … server sẽ có khả năng phục vụ nhiều client một lúc.
Và trong thực tế, async rất hay được sử dụng để làm những công việc như vậy.
Thư viện Asio hỗ trợ cả synchronous netwoking lẫn asynchronous netwoking.
Trong phần này mình sẽ hướng dẫn các bạn thực hành async căn bản với một Asio timer.
Các loại timer mà bạn có thể sử dụng:
– asio::deadline_timer
– asio::steady_timer
Sự khác nhau đó là steady_timer sẽ không bị ảnh hưởng khi clock của system đang chạy bị thay đổi, còn deadline_timer sẽ bị ảnh hưởng nếu ai đó thay đổi system time.
Trong ví dụ này mình sẽ sử dụng steady_timer cùng với thư viện chrono của C++11.
#define ASIO_STANDALONE // define this or Asio will use system's default time API instead of C++11 chrono #define ASIO_HAS_STD_CHRONO #include <chrono> #include <asio.hpp> #include <iostream> #include <thread> using namespace asio; class async_basic { public: async_basic(io_service&); ~async_basic(); void wait_five_secs(); void do_something(); private: steady_timer timer_; bool running_; void timeout(const std::error_code&); }; async_basic::async_basic(io_service& s) : timer_(s) { } void async_basic::wait_five_secs() { std::cout << "I'm waiting" << std::endl; // use this function or pass argument to timer's constructor to set expiry time timer_.expires_from_now(std::chrono::seconds(5)); timer_.async_wait(std::bind(&async_basic::timeout, this, std::placeholders::_1)); } void async_basic::do_something() { std::cout << "Done" << std::endl; } void async_basic::timeout(const std::error_code& e) { if (!e) { // check for errors std::cout << "I'm ready to work" << std::endl; do_something(); } } async_basic::~async_basic() { timer_.cancel(); } int main() { io_service service; async_basic b(service); b.wait_five_secs(); std::cout << "b is waiting, I'm not" << std::endl; service.run(); return 0; } |
Ở bài viết trước chúng ta đã biết cách sử dụng std::bind để tạo ra một Function Object từ địa chỉ của một hàm tự do. Trong code ví dụ trên có thể dễ dàng thấy cú pháp sử dụng với một hàm thành viên:
std::bind(địa_chỉ_hàm, địa_chỉ_object, danh_sách_tham_số);
std::placeholders::_1 là một tham số chưa biết trước, bởi function object sẽ được gọi ở trong tương lai. Nếu bạn có 3 tham số cho hàm này, bạn phải truyền lần lượt:
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3
Giải thích code:
Thông thường, khi chúng ta chờ đợi một khoảng thời gian nào đó, có thể dùng hàm sleep, thread sleep hoặc viết một vòng while và kiểm tra thời gian chạy vòng lặp.
Điều đó sẽ khiến cho toàn bộ chương trình bị block tại thời điểm đó.
Phải làm gì nếu mình muốn chỉ riêng 1 thứ gì đó tạm dừng nhưng tất cả những dòng lệnh khác vẫn chạy tiếp? Ví dụ ở trên là một minh họa về async. Object b (thực ra là một timer_ của b) sẽ chờ 5 giây, sau đó sẽ thực thi một công việc.
Hàm async_wait là một hàm asynchronous, nên nó đã trả về sau khi bạn gọi nhằm không block các thành phần khác. Vậy nên dòng lệnh phía dưới của b.wait_five_secs(); vẫn được thực thi, mặt khác, timer vẫn đang tính giờ và mình không sử dụng threads.
Đó chính là đặc điểm của async. Async không hề tương đương với multi-threading, nó khác với multi-threading và không bắt buộc phải multi-threading (ví dụ Java-Script cũng có async single-threaded). Và tất nhiên có thể kết hợp Async và threads với nhau.
Manh
March 16, 2016
C++ Networking