Nội dung
Biến điều kiện (Condition Variable) là một loại sự kiện được sử dụng để báo hiệu giữa hai hoặc nhiều luồng. Một hoặc nhiều luồng có thể đợi để nhận tín hiệu, trong khi một luồng khác có thể báo hiệu điều này.
Header file báo hiệu cho biến điều kiện trong C++11 là:
#include <condition_variable>
Cần phải sử dụng mutext kèm theo khi dùng biến điều kiện.
Làm thế nào mọi thứ thực sự làm việc với biến điều kiện?
Thread 1 sẽ gọi hàm wait() của biến điều kiện, hàm chứa mutex và kiểm tra điều kiện yêu cầu có được thỏa hay không. Nếu không, thread sẽ nhả khóa và tiếp tục chờ đợi biến điều kiện để nhận tín hiệu. Lúc này, thread bị block. Hàm wait() của biến điều kiện sẽ thực hiện công việc này theo cách thức nguyên tử (atomic manner).
Một thread khác, thread 2 chẳng hạn, báo hiệu biến điều kiện khi điều kiện được thỏa.
Một khi biến có điều kiện nhận được tín hiệu và báo cho thread 1 đang chờ. Sau đó, nó lấy lại khóa mutex một lần nữa và kiểm tra xem điều kiện liên quan đến biến điều kiện có thực sự được đáp ứng hay nếu đó là lời gọi của hàm cấp trên. Nếu có nhiều hơn một thread đang chờ thì notify_one sẽ chỉ bỏ chặn một thread. Nếu đó là một lời gọi ở cấp trên thì nó lại gọi hàm Wait().
Các hàm thành viên chính cho biến điều kiện
Wait()
Nó làm cho thread hiện tại bị chặn cho đến khi biến điều kiện được báo hiệu hoặc xảy ra đánh thức giả.
Nó nguyên bản sẽ giải phóng mutex đính kèm, chặn thread hiện tại và thêm nó vào danh sách các thread đang chờ trên đối tượng biến điều kiện hiện tại. Thread sẽ được bỏ chặn khi một số thread gọi notify_one() hoặc notify_all() trên cùng một đối tượng biến điều kiện. Nó cũng có thể được bỏ chặn một cách giả tạo, do đó, sau mỗi lần bỏ chặn, nó cần kiểm tra lại điều kiện.
Một callback được truyền dưới dạng tham số cho hàm này, nó sẽ được gọi để kiểm tra xem đó có phải là một lời gọi giả hay điều kiện thực sự được đáp ứng.
Khi các thread được mở khóa, hàm Wait() sẽ lấy lại khóa mutex và kiểm tra xem điều kiện thực sự có được đáp ứng hay không. Nếu điều kiện không được đáp ứng thì một lần nữa nó sẽ giải phóng mutex đính kèm, chặn thread hiện tại và thêm nó vào danh sách các thread đang chờ trên đối tượng biến điều kiện hiện tại.
notify_one()
Nếu bất kỳ thread nào đang chờ trên cùng một đối tượng biến điều kiện thì notify_one sẽ bỏ chặn một trong các thread chờ.
notify_all()
Nếu bất kỳ thread nào đang chờ trên cùng một đối tượng biến điều kiện thì notify_all sẽ bỏ chặn tất cả các thread chờ.
Hãy xem làm thế nào ta có thể xử lý bài toán đã thảo luận trong phần trước đó với biến điều kiện. Code được viết lại với biến điều kiện như sau,
#include <iostream>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
using namespace std::placeholders;
class Application
{
std::mutex m_mutex;
std::condition_variable m_condVar;
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;
// Notify the condition variable
m_condVar.notify_one();
}
bool isDataLoaded()
{
return m_bDataLoaded;
}
void mainTask()
{
std::cout << "Do Some Handshaking" << std::endl;
// Acquire the lock
std::unique_lock<std::mutex> mlock(m_mutex);
// Start waiting for the Condition Variable to get signaled
// Wait() will internally release the lock and make the thread to block
// As soon as condition variable get signaled, resume the thread and
// again acquire the lock. Then check if condition is met or not
// If condition is met then continue else again go in wait.
m_condVar.wait(mlock, std::bind(&Application::isDataLoaded, this));
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;
}
Để lại một bình luận