ฟิวเจอร์สกับสัญญา


138

ฉันสับสนกับความแตกต่างระหว่างอนาคตกับคำสัญญา

เห็นได้ชัดว่าพวกเขามีวิธีการและสิ่งต่าง ๆ แต่กรณีการใช้งานจริงเป็นอย่างไร?

ใช่ไหม?:

  • เมื่อฉันจัดการงาน async ฉันใช้อนาคตเพื่อรับค่า "ในอนาคต"
  • เมื่อฉันเป็นงาน async ฉันใช้สัญญาเป็นประเภทการส่งคืนเพื่อให้ผู้ใช้ได้รับอนาคตจากสัญญาของฉัน

1
ฉันเขียนเล็กน้อยเกี่ยวกับเรื่องนี้ในคำตอบนี้
Kerrek SB

1
เป็นไปได้ที่จะซ้ำกันของstd :: สัญญาคืออะไร?
Nicol Bolas

คำตอบ:


168

อนาคตและคำสัญญาเป็นสองด้านที่แยกจากกันของการดำเนินการแบบอะซิงโครนัส

std::promise ถูกใช้โดย "ผู้ผลิต / ผู้เขียน" ของการดำเนินการแบบอะซิงโครนัส

std::future ถูกใช้โดย "ผู้บริโภค / ผู้อ่าน" ของการดำเนินการแบบอะซิงโครนัส

เหตุผลที่แยกออกเป็น "อินเทอร์เฟซ" สองรายการนี้คือการซ่อนฟังก์ชัน "เขียน / ตั้งค่า" จาก "ผู้บริโภค / ผู้อ่าน"

auto promise = std::promise<std::string>();

auto producer = std::thread([&]
{
    promise.set_value("Hello World");
});

auto future = promise.get_future();

auto consumer = std::thread([&]
{
    std::cout << future.get();
});

producer.join();
consumer.join();

วิธีหนึ่ง (ไม่สมบูรณ์) ในการใช้ std :: async โดยใช้ std :: สัญญาอาจเป็น:

template<typename F>
auto async(F&& func) -> std::future<decltype(func())>
{
    typedef decltype(func()) result_type;

    auto promise = std::promise<result_type>();
    auto future  = promise.get_future();

    std::thread(std::bind([=](std::promise<result_type>& promise)
    {
        try
        {
            promise.set_value(func()); // Note: Will not work with std::promise<void>. Needs some meta-template programming which is out of scope for this question.
        }
        catch(...)
        {
            promise.set_exception(std::current_exception());
        }
    }, std::move(promise))).detach();

    return std::move(future);
}

การใช้std::packaged_taskซึ่งเป็นตัวช่วย (กล่าวคือโดยทั่วไปแล้วจะทำในสิ่งที่เราทำข้างต้น) รอบ ๆ ตัวstd::promiseคุณสามารถทำสิ่งต่อไปนี้ซึ่งสมบูรณ์กว่าและอาจเร็วกว่า

template<typename F>
auto async(F&& func) -> std::future<decltype(func())>
{
    auto task   = std::packaged_task<decltype(func())()>(std::forward<F>(func));
    auto future = task.get_future();

    std::thread(std::move(task)).detach();

    return std::move(future);
}

โปรดทราบว่าสิ่งนี้แตกต่างเล็กน้อยจากstd::asyncจุดstd::futureประสงค์ที่ส่งคืนเมื่อทำลายจริงบล็อกจนกว่าเธรดจะเสร็จสิ้น


3
@taras ชี้ให้เห็นว่าการกลับมาstd::move(something)ไม่มีประโยชน์และมันก็เจ็บ (N) RVO กำลังเปลี่ยนกลับการแก้ไขของเขา
polkovnikov.ph

ใน Visual Studio 2015 โปรดใช้ std :: cout << future.get (). c_str ();
Damian

8
สำหรับใครที่ยังงงดูคำตอบนี้
kawing-chiu

2
นั่นคือผู้ผลิตครั้งเดียว - ผู้บริโภค IMHO ที่ไม่ใช่ผู้ผลิต - รูปแบบผู้บริโภคจริงๆ
Martin Meeser
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.