Nội dung
Trong bài viết, ta sẽ bàn về việc cần phải xử lý sự kiện trong lập trình đa luồng. Đôi khi, sẽ một thread nào đó cần phải đợi sự kiện như là một điều kiện True để có thể thực thi, hoặc là đợi khi một công việc được hoàn thành bởi một thread khác. Hãy xem xét một ứng dụng sau,
Yêu cầu
Giả sử, ta đang xây dựng một ứng dụng có sử dụng mạng. Ứng dụng này có các công việc như sau
- Thực hiện một vài việc bắt tay với máy chủ
- Tải dữ liệu từ XML file
- Xử lý dữ liệu tải được từ XML file
Phân tích yêu cầu
Ta có thể thấy task 1 thì không phụ thuộc vào task nào khác. Task 3 thì phải chờ task load dữ liệu từ XML file hoàn thành thì mới có thể thực thi công việc của nó. Nghĩa là, task 1 và 2 có thể chạy song song bằng những thread khác nhau để tăng hiệu suất của ứng dụng.
Như vậy, ứng dụng có thể được chia làm 2 thread, với những công việc như sau:
Công việc của task 1:
- Thực hiện một vài việc bắt tay với máy chủ
- Đợi cho dữ liệu được tải từ XML file bởi thread 2
- Xử lý dữ liệu tải được từ XML file
Công việc của task 2:
- Tải dữ liệu từ XML file
- Thông báo cho những thread khác, cái mà đang đợi tin nhắn, chẳng hạn.
Thread 1 sẽ thực hiện vài tác vụ và đợi cho đến khi có một sự kiện hoặc điều kiện xảy ra. Sự kiện hoặc điều kiện này có thể là “dữ liệu đã được tải thành công”. Một khi Thread 1 nhận được tín hiệu này, nó sẽ tiếp tục xử lý dữ liệu vừa tải được. Thread 2 sẽ tải dữ liệu song song khi thread 1 đang thực hiện các cơ chế bắt tay với máy chủ. Khi tải xong, nó sẽ thông báo cho thread 1 bằng sự kiện này.
Lợi ích của việc đa luồng ở đây là gì?
Như vậy, trong khi thread 1 đạng bận với việc thực hiện các cơ chế bắt tay, thì song song đó, thread 2 đang tải dữ liệu từ XML file. Điều này là tăng hiệu suất của ứng dụng.
Cách tiếp cận
Tạo ra một biến Boolean toàn cục với giá trị mặc định là false. Đặt giá trị của nó thành true trong thread 2 và thread 1 sẽ tiếp tục kiểm tra giá trị của nó trong vòng lặp và ngay khi nó trở thành true, thread 1 sẽ tiếp tục xử lý dữ liệu. Nhưng vì nó là một biến toàn cục được chia sẻ bởi cả hai thread, nó cần đồng bộ hóa với mutex.
#include <iostream>
#include <thread>
#include <mutex>
class Application
{
std::mutex m_mutex;
bool m_bDataLoaded;
public:
Application()
{
m_bDataLoaded = false;
}
void loadData()
{
// Make This Thread sleep for 1 Second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::cout << "Loading Data from XML" << std::endl;
// Lock The Data structure
std::lock_guard<std::mutex> guard(m_mutex);
// Set the flag to true, means data is loaded
m_bDataLoaded = true;
}
void mainTask()
{
std::cout << "Do Some Handshaking" << std::endl;
// Acquire the Lock
m_mutex.lock();
// Check if flag is set to true or not
while (m_bDataLoaded != true)
{
// Release the lock
m_mutex.unlock();
//sleep for 100 milli seconds
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// Acquire the lock
m_mutex.lock();
}
// Release the lock
m_mutex.unlock();
//Doc processing on loaded Data
std::cout << "Do Processing On loaded Data" << std::endl;
}
};
int main()
{
Application app;
std::thread thread_1(&Application::mainTask, &app);
std::thread thread_2(&Application::loadData, &app);
thread_2.join();
thread_1.join();
return 0;
}
Kết quả là,
Tuy nhiên các tiếp cận này có một vài bất cập, thread sẽ tiếp tục lấy khóa và giải phóng nó chỉ để kiểm tra giá trị, do đó nó sẽ làm tiêu tốn chu kỳ CPU và cũng sẽ làm cho Thread 1 chậm, bởi vì nó cần phải lấy cùng một khóa để cập nhật cờ.
Vì vậy, rõ ràng chúng ta cần một cơ chế tốt hơn để đạt được điều này, nếu như bằng cách nào đó, thread 1 chỉ có thể dừng và chờ một thông tin nào đó được báo hiệu và một thread khác có thể báo hiệu thông tin đó, khiến thread 1 tiếp tục. Nó sẽ tiết kiệm được nhiều chu kỳ CPU và cho hiệu năng tốt hơn.
Chúng ta có thể đạt được điều này bằng cách sử dụng biến điều kiện (Condition variable). Biến điều kiện là một loại sự kiện được sử dụng để báo hiệu giữa 2 thread. Một thread có thể đợi cho nó nhận được tín hiệu, trong khi các thread khác có thể báo hiệu điều này. Trong bài viết tiếp theo ta sẽ sử dụng biến điều kiện làm giải pháp cho vấn đề này.
Để lại một bình luận