NÊN SỬ DỤNG MULTI THREADING THẾ NÀO


Sau khi đã biết cách viết chương trình multi-thread, giờ ta sẽ cùng bàn đến vấn đề quan trọng hơn: Khi nào nên dùng MT, và còn có lựa chọn nào khác nữa không?

ℹ️ Multi threading phù hợp với những trường hợp sau đây:

👉 Chương trình cần tính toán nhiều (sử dụng nhiều năng lực tính toán của CPU) và muốn tận dụng nhiều CPU hoặc core khác nhau, mỗi thread sẽ tính toán một bài toán riêng lẻ. Tuy nhiên lưu ý là không phải cứ tạo thread mới thì nó sẽ tận dụng được các nhân khác, nếu bạn có 2 thread nhưng được gán cho cùng một core/cpu thì hiệu năng cũng sẽ không tăng lên. Với các bài toán này, bạn nên tìm hiểu về các thư viện hỗ trợ lập trình song song trên nền tảng của bạn.Ngay cả khi bạn chỉ có một bài toán cần tính, bạn cũng nên đưa vào một thread chạy ngầm bởi 2 lý do:

– Chương trình sẽ không bị khóa cứng lại và trở nên “not responsive”, người dùng có thể theo dõi tiến độ, hoặc hủy nếu quá trình tính toán quá lâu.

– Các thread chạy ngầm thường sẽ được cấp time slide dài, giúp tăng hiệu quả làm việc. Một thử nghiệm tại IBM cho thấy trên Linux, hiệu năng tính toán tăng lên rõ rệt khi tăng giá trị của sched_min_granularity_ns được tăng lên (sched_min_granularity_ns là giá trị tối thiểu của 1 time slide)(https://events.static.linuxfound.org/…/lcna2011_rajan.pdf – trang 22).

👉 Chương trình cần có một giao diện có tính phản hồi: Có nhiều thao tác chiếm thời gian như: sao chép file, xử lý dữ liệu trong SQL, tạo các báo cáo… Các thao tác này thường sẽ mất một khoảng thời gian để hoàn thành. Cách tốt nhất là tạo một thread để thực hiện thao tác, trong quá trình hoạt động, thread có thể cập nhật trạng thái, % hoàn thành vào một biến chung, và bạn có thể định kỳ cập nhật trạng thái lên giao diện.

👉 Bản chất của chương trình là phải thực hiện nhiều công việc đồng thời. Một ví dụ là các web server: một server phải phục vụ nhiều client, nhiều client này có thể gửi request cùng một lúc. Bởi bản chất “đồng thời” này, nên việc chia ra mỗi thread xử lý một request sẽ tự nhiên và đơn giản hơn nhiều, so với việc bạn phải tự chuyển đổi thao tác xử lý giữa các kết nối khác nhau.

ℹ️ Nên nhớ, multi threading không phải là cây đũa thần giúp chương trình chạy nhanh hơn, bởi nó còn phụ thuộc vào bản chất của bài toán. Nếu đó là một chương trình thiên về tính toán, thì chương trình có thể nhanh hơn nếu bạn chia sẻ được công việc cho các CPU khác nhau, nhưng một khi tất cả CPU đã chạy 100% thì thêm thread nữa chỉ làm chương trình chạy chậm đi. Nếu một chương trình dựa nhiều vào các thao tác I/O, thì cổ chai nằm ở thiết bị của bạn, lúc đó thêm thread vào cũng không giải quyết được gì.

ℹ️ Bản thân việc dùng multi threading cũng có những nhược điểm, có thể kể ra như:- Chương trình trở nên phức tạp, khó theo dõi và debug.- Các cấu trúc dữ liệu dùng chung phải thêm cơ chế kiểm soát truy cập. (Sử dụng Mutex/Semaphore…)- Có khả năng bị dead lock: nếu thread A đang giữ tài nguyên R1 và chờ tài nguyên R2, trong khi đó thread B đang giữ R2 và chờ R1, 2 thread này sẽ rơi vào tình huống khóa lẫn nhau và không thoát ra được. (để giải quyết tình huống này, ta sẽ thêm thời gian tối đa khi chờ một tài nguyên).- Hai hoặc nhiều thread có thể cùng thực thi một đoạn lệnh, và dẫn đến những kết quả không xác định được (gọi là race condition), bạn có thể xem ví dụ sau:

int n = 0;
void inc_and_print() 
{
    while (n < 1000000)
    {
    n++;
    printLine(n);
    }
}

Nếu có hai thread cùng thực thi hàm inc_and_print, ta dễ dàng nhận ra một số giá trị sẽ không được in ra màn hình, một số giá trị khác, ngược lại sẽ được in ra 2 lần.(Để giải quyết ta dùng Mutex để kiểm soát số thread được thực thi đoạn lệnh đó trong một thời điểm)- Nhiều thread đồng nghĩa với việc thời gian chuyển đổi (context switch) lớn, làm giảm đi lượng thời gian thực sự có ích.

ℹ️ Trong thực tế, bạn luôn phải kiểm soát được số lượng thread được tạo ra, do vậy nếu không chắc chắn, bạn nên sử dụng mô hình Thread Pool.

ℹ️ Nếu thread của bạn cần làm các công việc phức tạp, tốn nhiều thời gian, hoặc tiềm ẩn nhiều vấn đề có thể gây ảnh hưởng toàn hệ thống, thì việc chuyển toàn bộ thread thành một chương trình độc lập là một ý tưởng không tồi. Giữa chương trình cha và con có thể giao tiếp thông qua tham số, kết quả trả về, hoặc các cơ chế IPC khác, thậm chí cả mutex và semaphore. Dùng cách này bạn có thể cách ly hoàn toàn chương trình con khỏi chương trình cha. Trình duyệt Chrome hiện tại cũng tiếp cận theo cách này để giúp tránh việc một tab bị lỗi gây treo toàn bộ ứng dụng.Tới đây có lẽ bạn đã rất thành thạo multi-thread rồi, chưa ư? Hãy thử làm bài tập này nhé:Viết một web server hỗ trợ GET và POST trong gói giao thức HTTP 1.1, cho phép phục vụ các trang web tĩnh (chỉ hỗ trợ file html, file ảnh hoặc script như: .JS, .JPG, .GIF, .HTM…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s