Trên thực tế, ví dụ bạn vừa đưa ra cho thấy sự khác biệt nếu bạn sử dụng một hàm khá dài, chẳng hạn như
//! sleeps for one second and returns 1
auto sleep = [](){
std::this_thread::sleep_for(std::chrono::seconds(1));
return 1;
};
Nhiệm vụ đóng gói
Bạn packaged_task
sẽ không tự mình bắt đầu, bạn phải gọi nó:
std::packaged_task<int()> task(sleep);
auto f = task.get_future();
task(); // invoke the function
// You have to wait until task returns. Since task calls sleep
// you will have to wait at least 1 second.
std::cout << "You can see this after 1 second\n";
// However, f.get() will be available, since task has already finished.
std::cout << f.get() << std::endl;
std::async
Mặt khác, std::async
với launch::async
sẽ cố gắng chạy tác vụ trong một luồng khác:
auto f = std::async(std::launch::async, sleep);
std::cout << "You can see this immediately!\n";
// However, the value of the future will be available after sleep has finished
// so f.get() can block up to 1 second.
std::cout << f.get() << "This will be shown after a second!\n";
Hạn chế
Nhưng trước khi bạn cố gắng sử dụng async
cho tất cả mọi thứ, hãy nhớ rằng tương lai được trả lại có trạng thái chia sẻ đặc biệt, đòi hỏi phải future::~future
ngăn chặn:
std::async(do_work1); // ~future blocks
std::async(do_work2); // ~future blocks
/* output: (assuming that do_work* log their progress)
do_work1() started;
do_work1() stopped;
do_work2() started;
do_work2() stopped;
*/
Vì vậy, nếu bạn muốn không đồng bộ thực sự, bạn cần giữ lại trả lại future
hoặc nếu bạn không quan tâm đến kết quả nếu hoàn cảnh thay đổi:
{
auto pizza = std::async(get_pizza);
/* ... */
if(need_to_go)
return; // ~future will block
else
eat(pizza.get());
}
Để biết thêm thông tin về điều này, hãy xem bài viết của Herb Sutter async
và~future
, trong đó mô tả vấn đề, và Scott Meyer std::futures
từ std::async
không đặc biệt , trong đó mô tả những hiểu biết. Cũng lưu ý rằng hành vi này đã được chỉ định trong C ++ 14 trở lên , nhưng cũng thường được thực hiện trong C ++ 11.
Khác biệt hơn nữa
Bằng cách sử dụng, std::async
bạn không thể chạy tác vụ của mình trên một luồng cụ thể nữa, nơi std::packaged_task
có thể được chuyển sang các luồng khác.
std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::thread myThread(std::move(task),2,3);
std::cout << f.get() << "\n";
Ngoài ra, packaged_task
cần phải được gọi trước khi bạn gọi f.get()
, nếu không chương trình của bạn sẽ đóng băng vì tương lai sẽ không bao giờ sẵn sàng:
std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);
TL; DR
Sử dụng std::async
nếu bạn muốn thực hiện một số việc và không thực sự quan tâm khi hoàn thành và std::packaged_task
nếu bạn muốn kết thúc mọi thứ để chuyển chúng sang các chủ đề khác hoặc gọi chúng sau. Hoặc, để trích dẫn Christian :
Cuối cùng, a std::packaged_task
chỉ là một tính năng cấp thấp hơn để thực hiện std::async
(đó là lý do tại sao nó có thể làm được nhiều hơn std::async
nếu được sử dụng cùng với các công cụ cấp thấp khác, như std::thread
). Nói một cách đơn giản std::packaged_task
là một std::function
liên kết đến a std::future
và std::async
kết thúc và gọi một std::packaged_task
(có thể trong một luồng khác).