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_tasksẽ 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::asyncvới launch::asyncsẽ 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 asynccho 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::~futurengă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 futurehoặ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 asyncvà~future , trong đó mô tả vấn đề, và Scott Meyer std::futurestừ std::asynckhô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::asyncbạ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_taskcó 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_taskcầ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::asyncnế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_tasknế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_taskchỉ 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::asyncnế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_tasklà một std::functionliên kết đến a std::futurevà std::asynckết thúc và gọi một std::packaged_task(có thể trong một luồng khác).