Nội dung
std::packaged_task<>
std :: pack_task <> là một lớp template và đại diện cho task thực thi không đồng bộ. Nó bao gồm:
- Một thực thể có thể gọi được. Đó có thể là hàm, hàm lambda hoặc đối tượng hàm.
- Một trạng thái chia sẻ lưu trữ giá trị được trả về hoặc ném ngoại lệ bằng cách gọi hàm callback.
Giả sử chúng ta hiện có một hàm hiện tìm nạp dữ liệu từ DB và trả về,
// Fetch some data from DB
std::string getDataFromDB( std::string token)
{
// Do some stuff to fetch the data
std::string data = "Data fetched from DB by Filter :: " + token;
return data;
}
Bây giờ ta muốn thực hiện chức năng này trong một thread riêng biệt. Nhưng làm thế nào chúng ta sẽ lấy kết quả hoặc ngoại lệ trả về trong thread chính sau khi thread này kết thúc?
Một cách là thay đổi khai báo hàm và truyền std::promise<> trong hàm. Trước khi chuyển đối tượng std::promise<> cho thread, hãy tìm nạp std::future<> và đặt nó trong thread chính. Bây giờ, trước khi thread trả về giá trị của nó, nó nên set giá trị cho std::prmoise<> đã truyền, để nó có thể có sẵn cho đối tượng std::future<> trong thread chính. Hãy xem lại bài viết C++11 Multithreading – P.8: Giá trị trả về từ Thread, std::future và std::promise để biết chi tiết hơn về giải pháp này.
Sử dụng packaged_task<> với hàm để tạo các tác vụ bất đồng bộ
std::packaged_task<> có thể bao một hàm bình thường và làm cho nó có thể chạy dưới dạng hàm bất đồng bộ. Khi std::packaged_task<> được gọi trong một thread riêng biệt, nó sẽ gọi lại hàm callback được liên kết và lưu trữ giá trị/ngoại lệ trả về trong trạng thái chia sẻ bên trong của nó. Giá trị này có thể được truy cập trong thread khác hoặc hàm main thông qua đối tượng std::future<>. Hãy cùng nhau tạo ra một std::packaged_task<> từ hàm được đề cập ở trên, thực thi trong thread riêng biệt và tìm nạp kết quả từ đối tượng std::future<> của nó.
Tạo ra một đối tượng std::packaged_task<>
std::packaged_task<> là một lớp template, vì vậy ta cần truyền đối số template cho nó, như là kiểu của đối số hàm chẳng hạn.
// Create a packaged_task<> that encapsulated the callback i.e. a function
std::packaged_task<std::string (std::string)> task(getDataFromDB);
Tìm nạp cho đối tượng std::future<>,
// Fetch the associated future<> from packaged_task<>
std::future<std::string> result = task.get_future();
Truyền packaged_task<> đến một thread,
// Pass the packaged_task to thread to run asynchronously
std::thread th(std::move(task), "Arg");
Vì packaged_task chỉ có thể di chuyển và không thể sao chép, do đó ta phải tìm nạp đối tượngstd::future<> từ nó trước khi chuyển nó sang thread. Thread sẽ thực thi tác vụ này, mà bên trong gọi các thực thể có thể gọi được liên kết, tức là hàm getDataFromDB(). Khi hàm này trả giá trị, std::packaged_task<> đặt nó thành trạng thái chia sẻ và kết quả hay ngoại lệ được trả về bởi getDataFromDB() sẽ có sẵn trong đối tượng std::future<>. Trong hàm main, lấy kết quả từ std::future<> này.
// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
std::string data = result.get();
Hàm get() sẽ chặn việc gọi thread cho đến khi hàm được gọi trả về giá trị và std::packaged_task<> đặt dữ liệu ở trạng thái có thể chia sẻ.
#include <iostream>
#include <thread>
#include <future>
#include <string>
// Fetch some data from DB
std::string getDataFromDB( std::string token)
{
// Do some stuff to fetch the data
std::string data = "Data fetched from DB by Filter :: " + token;
return data;
}
int main()
{
// Create a packaged_task<> that encapsulated the callback i.e. a function
std::packaged_task<std::string (std::string)> task(getDataFromDB);
// Fetch the associated future<> from packaged_task<>
std::future<std::string> result = task.get_future();
// Pass the packaged_task to thread to run asynchronously
std::thread th(std::move(task), "Arg");
// Join the thread. Its blocking and returns when thread is finished.
th.join();
// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
std::string data = result.get();
std::cout << data << std::endl;
return 0;
}
Kết quả là : Data fetched from DB by Filter :: Arg
Tương tự như trên, chúng ta cũng có thể tạo một package_task với hàm lambda và các đối tượng hàm.
Tạo package_task với hàm lambda
#include <iostream>
#include <thread>
#include <future>
#include <string>
int main()
{
// Create a packaged_task<> that encapsulated a lambda function
std::packaged_task<std::string (std::string)> task([](std::string token){
// Do some stuff to fetch the data
std::string data = "Data From " + token;
return data;
});
// Fetch the associated future<> from packaged_task<>
std::future<std::string> result = task.get_future();
// Pass the packaged_task to thread to run asynchronously
std::thread th(std::move(task), "Arg");
// Join the thread. Its blocking and returns when thread is finished.
th.join();
// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
std::string data = result.get();
std::cout << data << std::endl;
return 0;
}
Tạo package_task với đối tượng hàm
#include <iostream>
#include <thread>
#include <future>
#include <string>
/*
* Function Object to Fetch Data from DB
*/
struct DBDataFetcher
{
std::string operator()(std::string token)
{
// Do some stuff to fetch the data
std::string data = "Data From " + token;
return data;
}
};
int main()
{
// Create a packaged_task<> that encapsulated a lambda function
std::packaged_task<std::string(std::string)> task(std::move(DBDataFetcher()));
// Fetch the associated future<> from packaged_task<>
std::future<std::string> result = task.get_future();
// Pass the packaged_task to thread to run asynchronously
std::thread th(std::move(task), "Arg");
// Join the thread. Its blocking and returns when thread is finished.
th.join();
// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
std::string data = result.get();
std::cout << data << std::endl;
return 0;
}
Để lại một bình luận