স্ট্যান্ড :: প্রতিশ্রুতি কি?


384

আমি সি ++ 11 এর সাথে মোটামুটি পরিচিত std::thread , std::asyncএবং std::futureউপাদান (যেমন দেখুন এই উত্তর ), যা হয় সোজা সম্মুখগামী।

যাইহোক, আমি কি যথেষ্ট বুঝতে পারি না std::promise , এটি কী করে এবং কোন পরিস্থিতিতে এটি সর্বোত্তমভাবে ব্যবহৃত হয়। স্ট্যান্ডার্ড ডকুমেন্টটিতে নিজেই এর ক্লাসের সিনোপসিসের বাইরে পুরো প্রচুর তথ্য ধারণ করে না এবং কেবল :: থ্রেডও দেয় না ।

কেউ দয়া করে একটি পরিস্থিতিতে একটি সংক্ষিপ্ত, সংক্ষিপ্ত উদাহরণ দিতে পারে যেখানে একটি std::promise যেখানে এটির প্রয়োজন সবচেয়ে বেশি এবং যেখানে এটি সবচেয়ে প্রতিচ্ছবিযুক্ত সমাধান?


2
এটির সাথে এখানে কিছু কোড রয়েছে: en.cppreferences.com/w/cpp/thread/future
chris

58
সত্যিই, সত্যিই সংক্ষিপ্ত সংস্করণ হল: std::promiseযেখানে std::futures থেকে আসা। std::futureআপনাকে প্রতিশ্রুতি দেওয়া হয়েছে এমন একটি মান পুনরুদ্ধার করার অনুমতি দেয় । আপনি কল যখন get()ভবিষ্যতে, এটি মালিক পর্যন্ত অপেক্ষা std::promiseযা দিয়ে এটা মান (কল করে সেট করে set_valueপ্রতিশ্রুতি তে) খুলুন। যদি কোনও মান সেট হওয়ার আগে প্রতিশ্রুতিটি নষ্ট হয়ে যায় এবং আপনি get()সেই প্রতিশ্রুতির সাথে যুক্ত ভবিষ্যতের দিকে আহ্বান করেন তবে আপনি একটি std::broken_promiseব্যতিক্রম পাবেন কারণ আপনাকে একটি মূল্য দেওয়ার প্রতিশ্রুতি দেওয়া হয়েছিল, তবে এটি একটি হওয়া আপনার পক্ষে অসম্ভব।
জেমস ম্যাকনেলিস

14
আমি যে সুপারিশ, যদি আপনি করতে পারেন / চাই, কটাক্ষপাত করা কার্যরত সি ++ concurrency দ্বারা এন্থনি উইলিয়ামস
ডেভিড রদ্রিগেজ - dribeas

32
@ কেরেকএসবি std::broken_promiseহ'ল স্ট্যান্ডার্ড লাইব্রেরির সেরা নাম চিহ্নিতকারী। এবং নেই std::atomic_future
কুবিবি

3
ডাউনভোটার, আপনার আপত্তি বুঝানোর জন্য যত্ন?
কেরেক এসবি

উত্তর:


182

[ফিউচারস.স্টেট] এর কথায় একটি std::futureহ'ল অ্যাসিনক্রোনাস রিটার্ন অবজেক্ট ("এমন একটি বস্তু যা একটি ভাগ করা রাষ্ট্র থেকে ফলাফলগুলি পড়ে") এবং একটি std::promiseএকটি অ্যাসিনক্রোনাস সরবরাহকারী ("এমন একটি বস্তু যা ভাগ করে নেওয়া অবস্থার ফলাফল সরবরাহ করে") অর্থাৎ একটি প্রতিশ্রুতি হ'ল আপনি যে ফলাফলটিতে ফলাফল স্থাপন করেছেন, যাতে আপনি তা পেতে পারেন এটি সম্পর্কিত ভবিষ্যত থেকে ।

অ্যাসিঙ্ক্রোনাস সরবরাহকারী হ'ল প্রারম্ভিকভাবে ভবিষ্যতের দ্বারা বোঝানো অংশীদারি রাষ্ট্র তৈরি করে। std::promiseএক ধরণের অ্যাসিক্রোনাস সরবরাহকারী, std::packaged_taskঅন্যটি এবং এর অভ্যন্তরীণ বিশদটি std::asyncঅন্যরকম। এগুলির প্রত্যেকটি একটি ভাগ করা রাষ্ট্র তৈরি করতে এবং আপনাকে একটি দিতে পারেstd::future সেই রাজ্যটি ভাগ করে দেয় এবং রাষ্ট্রটিকে প্রস্তুত করে তুলতে পারে।

std::asyncহ'ল একটি উচ্চ-স্তরের সুবিধার্থে ইউটিলিটি যা আপনাকে একটি অ্যাসিনক্রোনাস রেজাল্ট অবজেক্ট দেয় এবং অভ্যন্তরীণভাবে অ্যাসিনক্রোনাস সরবরাহকারী তৈরি করার এবং টাস্ক শেষ হলে ভাগ করে নেওয়া রাষ্ট্রকে প্রস্তুত করার যত্ন নেয়। আপনি এটিকে একটি std::packaged_task(বা std::bindএবং ক std::promise) এবং এ দিয়ে অনুকরণ std::threadকরতে পারেন তবে এটি নিরাপদ এবং ব্যবহার সহজ easierstd::async

std::promiseআপনি কিছুটা নিচু স্তরের, কারণ আপনি যখন ভবিষ্যতে একটি অ্যাসিনক্রোনাস ফলাফলটি পাস করতে চান, তবে ফলাফলটি প্রস্তুত করে এমন কোডটি পাসের জন্য উপযুক্ত একটি ফাংশনে আবৃত হতে পারে না std::async। উদাহরণস্বরূপ, আপনার বেশ কয়েকটি promiseগুলি এবং সম্পর্কিত futureএসগুলির একটি অ্যারে থাকতে পারে এবং একটি একক থ্রেড থাকতে পারে যা বেশ কয়েকটি গণনা করে এবং প্রতিটি প্রতিশ্রুতিতে ফলাফল নির্ধারণ করে। asyncকেবলমাত্র আপনাকে একক ফলাফল ফেরত দেওয়ার অনুমতি দেবে, কয়েকটি ফেরত দিতে আপনাকে asyncবেশ কয়েকবার কল করতে হবে যা সংস্থানগুলি নষ্ট করতে পারে।


10
বর্জ্য সংস্থান হতে পারে? ভুল হতে পারে, যদি সেই কোডটি সমান্তরাল করা না যায়।
কুকুরছানা

"অ্যাসিনক্রোনাস রিটার্ন" এবং "শেয়ার স্টেটের রিড রেজাল্ট" বেশিরভাগ অর্থেগোনাল, যা প্রথম বাক্যটিকে কিছুটা বিভ্রান্ত করে তোলে। আপনি কি বলতে চাইছেন যে রাষ্ট্রের ভাগাভাগি ভবিষ্যৎ এবং প্রতিশ্রুতির মধ্যে রয়েছে? যদি তা হয় তবে দয়া করে শুরু থেকেই স্পষ্ট করে বলুন।
einpoklum

@ আইনপোকলুম আপনি শেষ শব্দের আগে "অ্যাসিনক্রোনাস রিটার্ন অবজেক্ট" পড়া বন্ধ করলেন কেন? আমি স্ট্যান্ডার্ড এর পরিভাষা উদ্ধৃত করছি। এ অ্যাসিক্রোনাস রিটার্ন অবজেক্টেরfuture একটি কংক্রিট উদাহরণ , যা এমন একটি বস্তু যা ভাগ ফলাফলের সাথে ভাগ করে নেওয়ার ফলে অ্যাসিক্রোনোনালি ফিরে আসে। এ হ'ল অ্যাসিক্রোনাস সরবরাহকারীর একটি দৃ example় উদাহরণ , যা এমন একটি বস্তু যা ভাগ করা রাষ্ট্রের জন্য একটি মূল্য লিখায়, যা অ্যাসিঙ্ক্রোনাসলি পড়তে পারে। আমি যা লিখেছি তা বোঝাতে চেয়েছি। promise
জোনাথন ওয়াকলি

496

আমি পরিস্থিতিটি এখন কিছুটা ভালভাবে বুঝতে পেরেছি (এখানে উত্তরগুলির কারণে অল্প পরিমাণে নয়!), তাই আমি ভেবেছিলাম আমি নিজের লেখাটি একটু যোগ করব।


সি ++ 11 এ দুটি স্বতন্ত্র, যদিও সম্পর্কিত ধারণা রয়েছে: অ্যাসিনক্রোনাস গণনা (একটি ফাংশন যা অন্য কোথাও বলা হয়), এবং একযোগে সম্পাদন (একটি থ্রেড , যা কিছু একযোগে কাজ করে)। দুটি হ'ল কিছুটা অরথোগোনাল ধারণা। অ্যাসিঙ্ক্রোনাস গণনা হ'ল ফাংশন কলের কেবল আলাদা স্বাদ, যখন একটি থ্রেড একটি বাস্তবায়ন প্রসঙ্গ। থ্রেডগুলি তাদের নিজস্বভাবে কার্যকর, তবে এই আলোচনার উদ্দেশ্যটির জন্য, আমি তাদের বাস্তবায়ন বিশদ হিসাবে বিবেচনা করব।


অ্যাসিনক্রোনাস গণনার জন্য বিমূর্তনের একটি শ্রেণিবিন্যাস রয়েছে। উদাহরণস্বরূপ, ধরুন আমাদের একটি ফাংশন রয়েছে যা কিছু যুক্তি দেখায়:

int foo(double, char, bool);

প্রথমে, আমাদের কাছে টেমপ্লেট রয়েছে std::future<T>, যা ভবিষ্যতের ধরণের মানের প্রতিনিধিত্ব করে T। সদস্যটি ফাংশনের মাধ্যমে মানটি পুনরুদ্ধার করা যায় get(), যা ফলাফলটির জন্য অপেক্ষা করে কার্যকরভাবে প্রোগ্রামটিকে সিঙ্ক্রোনাইজ করে। বিকল্পভাবে, একটি ভবিষ্যত সমর্থন করে wait_for(), যা ফলাফল ইতিমধ্যে উপলব্ধ কিনা তা অনুসন্ধানের জন্য ব্যবহার করা যেতে পারে। ফিউচারগুলি সাধারণ রিটার্নের ধরণের অ্যাসিনক্রোনাস ড্রপ-ইন প্রতিস্থাপন হিসাবে ভাবা উচিত। আমাদের উদাহরণ ফাংশন জন্য, আমরা একটি আশা করিstd::future<int>

এখন, স্তরক্রমের দিকে, সর্বোচ্চ থেকে নিম্নতম স্তরের:

  1. std::async: অ্যাসিঙ্ক্রোনাস গণনা সম্পাদনের সর্বাধিক সুবিধাজনক এবং সোজা-সামনের উপায় হ'ল asyncফাংশন টেমপ্লেটের মাধ্যমে যা ম্যাচের ভবিষ্যতে অবিলম্বে ফিরে আসে:

    auto fut = std::async(foo, 1.5, 'x', false);  // is a std::future<int>

    বিশদগুলির উপর আমাদের খুব কম নিয়ন্ত্রণ রয়েছে। বিশেষত, আমরা এমনকি ফাংশনটি যুগপত, ক্রমিকভাবে get()বা অন্য কোনও কালো যাদু দ্বারা সম্পাদিত হয় কিনা তাও জানি না । তবে প্রয়োজনে ফলাফল সহজেই পাওয়া যায়:

    auto res = fut.get();  // is an int
  2. আমরা এখন বিবেচনা করতে পারি যে কীভাবে এমন কিছু বাস্তবায়ন করা যায় asyncতবে এটি একটি ফ্যাশনে আমরা নিয়ন্ত্রণ করি। উদাহরণস্বরূপ, আমরা জোর দিয়ে বলতে পারি যে ফাংশনটি একটি পৃথক থ্রেডে কার্যকর করা হবে। আমরা ইতিমধ্যে জানি যে আমরা std::threadক্লাসের মাধ্যমে একটি পৃথক থ্রেড সরবরাহ করতে পারি ।

    বিমূর্তনের পরবর্তী নিম্ন স্তরের ঠিক এটি করে: std::packaged_task। এটি একটি টেমপ্লেট যা কোনও ফাংশনকে আবৃত করে এবং ফাংশনগুলির রিটার্ন মানটির জন্য ভবিষ্যত সরবরাহ করে তবে অবজেক্টটি নিজেই কলযোগ্য এবং এটিকে কল করা ব্যবহারকারীর বিবেচনার ভিত্তিতে। আমরা এটির মতো এটি সেট আপ করতে পারি:

    std::packaged_task<int(double, char, bool)> tsk(foo);
    
    auto fut = tsk.get_future();    // is a std::future<int>

    আমরা যখন টাস্কটি কল করি এবং কলটি শেষ হয় তখন ভবিষ্যত প্রস্তুত হয়। এটি আলাদা থ্রেডের জন্য আদর্শ কাজ। কাজটি থ্রেডে স্থানান্তরিত করার জন্য আমাদের কেবল নিশ্চিত করতে হবে :

    std::thread thr(std::move(tsk), 1.5, 'x', false);

    থ্রেড সঙ্গে সঙ্গে চলতে শুরু করে। আমরা হয় detachতা রাখতে পারি, বা joinসুযোগের শেষে থাকতে পারি, বা যখনই (যেমন অ্যান্টনি উইলিয়ামসের scoped_threadমোড়ক ব্যবহার করে , যা সত্যই স্ট্যান্ডার্ড লাইব্রেরিতে থাকা উচিত)। ব্যবহারের বিশদটি std::threadআমাদের এখানে উদ্বিগ্ন করে না; শুধু যোগদান বা thrঅবশেষে বিচ্ছেদ নিশ্চিত । গুরুত্বপূর্ণ বিষয় হ'ল যখনই ফাংশন কল শেষ হয়, তখন আমাদের ফলাফল প্রস্তুত থাকে:

    auto res = fut.get();  // as before
  3. এখন আমরা সর্বনিম্ন পর্যায়ে নেমে করছি: আমরা চাই কিভাবে বাস্তবায়ন প্যাকেজ কাজের? এটি এখানেই std::promiseআসে The প্রতিশ্রুতি হ'ল ভবিষ্যতের সাথে যোগাযোগের জন্য বিল্ডিং ব্লক। প্রধান পদক্ষেপগুলি হ'ল:

    • কলিং থ্রেড একটি প্রতিশ্রুতি দেয়।

    • কলিং থ্রেড প্রতিশ্রুতি থেকে ভবিষ্যত প্রাপ্ত করে।

    • প্রতিশ্রুতি, ফাংশন আর্গুমেন্ট সহ, একটি পৃথক থ্রেডে সরানো হয়।

    • নতুন থ্রেডটি কার্য সম্পাদন করে এবং প্রতিশ্রুতি পূর্ণ করে।

    • মূল থ্রেড ফলাফল পুনরুদ্ধার করে।

    উদাহরণ হিসাবে, এখানে আমাদের নিজস্ব নিজস্ব "প্যাকেজড টাস্ক" রয়েছে:

    template <typename> class my_task;
    
    template <typename R, typename ...Args>
    class my_task<R(Args...)>
    {
        std::function<R(Args...)> fn;
        std::promise<R> pr;             // the promise of the result
    public:
        template <typename ...Ts>
        explicit my_task(Ts &&... ts) : fn(std::forward<Ts>(ts)...) { }
    
        template <typename ...Ts>
        void operator()(Ts &&... ts)
        {
            pr.set_value(fn(std::forward<Ts>(ts)...));  // fulfill the promise
        }
    
        std::future<R> get_future() { return pr.get_future(); }
    
        // disable copy, default move
    };

    এই টেম্পলেটটির ব্যবহার মূলত একই রকম std::packaged_task । নোট করুন যে পুরো টাস্কটি সরিয়ে নেওয়া প্রতিশ্রুতিটি সরিয়ে দেয়। অধিকতর অ্যাডহক পরিস্থিতিতে, কেউ প্রতিশ্রুতিযুক্ত বস্তুকে স্পষ্টভাবে নতুন থ্রেডে নিয়ে যেতে পারে এবং এটি থ্রেড ফাংশনের একটি ফাংশন আর্গুমেন্ট হিসাবে তৈরি করতে পারে, তবে উপরের মতো একটি টাস্ক র‌্যাপারটি আরও নমনীয় এবং স্বল্প হস্তক্ষেপের মতো মনে হয়।


ব্যতিক্রম করা

প্রতিশ্রুতি নিবিড়ভাবে ব্যতিক্রম সম্পর্কিত। একমাত্র প্রতিশ্রুতি দেওয়ার ইন্টারফেসটি তার রাজ্যটি সম্পূর্ণরূপে জানাতে যথেষ্ট নয়, তাই যখনই কোনও প্রতিশ্রুতির কোনও ক্রিয়াকলাপটি বোঝায় না তখন ব্যতিক্রমগুলি ছুঁড়ে দেওয়া হয়। সমস্ত ব্যতিক্রম প্রকারের std::future_error, যা থেকে প্রাপ্ত std::logic_error। প্রথমে কিছু প্রতিবন্ধকতার বর্ণনা:

  • একটি ডিফল্ট-নির্মিত প্রতিশ্রুতি নিষ্ক্রিয়। নিষ্ক্রিয় প্রতিশ্রুতি পরিণতি ছাড়াই মারা যেতে পারে।

  • ভবিষ্যতের মাধ্যমে প্রাপ্ত হলে একটি প্রতিশ্রুতি সক্রিয় হয় get_future()। তবে, কেবলমাত্র একটি ভবিষ্যত পাওয়া যেতে পারে!

  • একটি প্রতিশ্রুতি হয় হয় এর মাধ্যমে সন্তুষ্ট হতে হবে set_value()বা set_exception()তার জীবনকাল শেষ হওয়ার আগেই তার ব্যতিক্রম সেট করতে হবে যদি তার ভবিষ্যতটি গ্রাস করতে হয়। একটি সন্তুষ্ট প্রতিশ্রুতি পরিণতি ছাড়াই মারা যেতে পারে এবং get()ভবিষ্যতে উপলব্ধ হয়। একটি ব্যতিক্রম সহ একটি প্রতিশ্রুতি get()ভবিষ্যতে ডেকে সঞ্চিত ব্যতিক্রম বাড়িয়ে তুলবে । যদি প্রতিশ্রুতি কোনও মূল্য বা ব্যতিক্রম না দিয়ে মারা যায়, get()ভবিষ্যতের দিকে আহ্বান করা একটি "ভাঙা প্রতিশ্রুতি" ব্যতিক্রম উত্থাপন করবে।

এই বিভিন্ন ব্যতিক্রমী আচরণগুলি প্রদর্শনের জন্য এখানে একটি সামান্য টেস্ট সিরিজ দেওয়া আছে। প্রথম, জোতা:

#include <iostream>
#include <future>
#include <exception>
#include <stdexcept>

int test();

int main()
{
    try
    {
        return test();
    }
    catch (std::future_error const & e)
    {
        std::cout << "Future error: " << e.what() << " / " << e.code() << std::endl;
    }
    catch (std::exception const & e)
    {
        std::cout << "Standard exception: " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cout << "Unknown exception." << std::endl;
    }
}

এখন পরীক্ষা।

কেস 1: নিষ্ক্রিয় প্রতিশ্রুতি

int test()
{
    std::promise<int> pr;
    return 0;
}
// fine, no problems

কেস 2: সক্রিয় প্রতিশ্রুতি, অব্যবহৃত

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();
    return 0;
}
// fine, no problems; fut.get() would block indefinitely

কেস 3: অনেক বেশি ফিউচার

int test()
{
    std::promise<int> pr;
    auto fut1 = pr.get_future();
    auto fut2 = pr.get_future();  //   Error: "Future already retrieved"
    return 0;
}

মামলা 4: সন্তুষ্ট প্রতিশ্রুতি

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
    }

    return fut.get();
}
// Fine, returns "10".

কেস 5: অত্যধিক সন্তুষ্টি

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
        pr2.set_value(10);  // Error: "Promise already satisfied"
    }

    return fut.get();
}

আছে যদি একাধিক একই ব্যতিক্রম ফেলা হয় পারেন এর set_valueবা set_exception

মামলা 6: ব্যতিক্রম

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_exception(std::make_exception_ptr(std::runtime_error("Booboo")));
    }

    return fut.get();
}
// throws the runtime_error exception

মামলা 7: ভাঙা প্রতিশ্রুতি

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
    }   // Error: "broken promise"

    return fut.get();
}

আপনি বলেছিলেন "... যা ফলাফলের জন্য অপেক্ষা করে কার্যকরভাবে প্রোগ্রামটিকে সিঙ্ক্রোনাইজ করে।" । "সিঙ্ক্রোনাইজ" এর অর্থ কী? পুরো বিবৃতিটির অর্থ কী? আমি এটি বুঝতে অক্ষম। এই অভিধান এন্ট্রি থেকে "সিঙ্ক্রোনাইজ" অর্থের কোনওটিই আমাকে বাক্যটি বুঝতে সহায়তা করে না। শুধু "অপেক্ষার" অর্থ কি "সিঙ্ক্রোনাইজেশন"? প্রতিটি অপেক্ষা কি সিনক্রোনাইজ হয়? আমি মনে করি আপনার আজ্ঞাটি আমি আংশিকভাবে বুঝতে পেরেছি তবে আপনি আসলে কী বোঝাতে চাই তা নিশ্চিত not
নওয়াজ

9
সুন্দর উত্তর, আপনার সহায়তার জন্য ধন্যবাদ
স্টিরিও ম্যাচিং

1
@ ফেলিক্সডমব্যাক: পারফেক্ট ফরওয়ার্ডিং ইত্যাদির std::functionঅনেক কনস্ট্রাক্টর রয়েছে; এর ভোক্তার কাছে তাদের প্রকাশ না করার কোনও কারণ নেই my_task
কেরেক এসবি

1
@ ডেভিডভি: প্রতিক্রিয়ার জন্য ধন্যবাদ! হ্যাঁ, এটি পরীক্ষার ক্ষেত্রে 7: আপনি যদি মান বা ব্যতিক্রম নির্ধারণ না করে প্রতিশ্রুতিটি নষ্ট করেন, তবে get()ভবিষ্যতে ফোন করা একটি ব্যতিক্রম উত্থাপন করে। আমি এটি "ধ্বংস হওয়ার আগে" যোগ করে এটি পরিষ্কার করব; যদি এটি যথেষ্ট পরিষ্কার হয় তবে আমাকে জানান know
কেরেক এসবি

3
অবশেষে got()আমার আশ্চর্যজনক ব্যাখ্যাটির futureউপর থ্রেড সমর্থন লাইব্রেরিটি ছুঁড়ে ফেলার জন্য promise!
রোদে চাঁদ

33

বার্তোসজ মাইলিউস্কি একটি ভাল লেখার ব্যবস্থা করে।

সি ++ ফিউচারগুলির প্রয়োগকে ছোট ছোট ব্লকের একটি সেটে বিভক্ত করে

std :: প্রতিশ্রুতি এই অংশগুলির মধ্যে একটি।

প্রতিশ্রুতি থ্রেড থেকে ফাংশন ভবিষ্যতে ক্যাশে হওয়া কোনও ফাংশন সম্পাদন করে থ্রেড থেকে রিটার্ন মান (বা একটি ব্যতিক্রম) পাস করার জন্য একটি বাহন।

...

ভবিষ্যত হ'ল প্রতিশ্রুতি চ্যানেলের প্রাপ্তির শেষের দিকে নির্মিত সিঙ্ক্রোনাইজেশন অবজেক্ট।

সুতরাং, যদি আপনি কোনও ভবিষ্যত ব্যবহার করতে চান তবে আপনি একটি প্রতিশ্রুতি দিয়ে শেষ করেন যা আপনি অ্যাসিক্রোনাস প্রসেসিংয়ের ফলাফল পেতে ব্যবহার করেন।

পৃষ্ঠাটির একটি উদাহরণ:

promise<int> intPromise;
future<int> intFuture = intPromise.get_future();
std::thread t(asyncFun, std::move(intPromise));
// do some other stuff
int result = intFuture.get(); // may throw MyException

4
থ্রেডের কনস্ট্রাক্টরে প্রতিশ্রুতি দেখে অবশেষে পেনি ড্রপ হয়ে গেল। বার্তোজের নিবন্ধটি সম্ভবত সবচেয়ে বড় নয়, তবে এটি কীভাবে উপাদানগুলি একত্রিত করে তা ব্যাখ্যা করে। ধন্যবাদ।
কেরেক এসবি

28

মোটামুটিভাবে আপনি একটি std::promiseএর অন্য প্রান্ত হিসাবে বিবেচনা করতে পারেন std::future(এটি মিথ্যা , তবে উদাহরণের জন্য আপনি ভাবতে পারেন যে এটি ছিল)। যোগাযোগের চ্যানেলের গ্রাহক std::futureপ্রান্তটি শেয়ারড স্টেট থেকে ডেটাম গ্রাস করতে ব্যবহার করবে , অন্যদিকে প্রযোজক থ্রেড std::promiseভাগ করে নেওয়া অবস্থায় লিখতে ব্যবহার করবে ।


12
@ কেরেকএসবি: std::asyncধারণা অনুসারে (এটি স্ট্যান্ডার্ড দ্বারা বাধ্যতামূলক নয়) এমন একটি ফাংশন হিসাবে বোঝা যায় যা std::promiseএকটি থ্রেড পুলে ধাক্কা দেয় (ধরণের, একটি থ্রেড পুল হতে পারে, একটি নতুন থ্রেড হতে পারে, ...) এবং ফিরে আসে যুক্ত std::futureকলারের কাছে। ক্লায়েন্টের দিকে আপনি অপেক্ষায় থাকতেন std::futureএবং অন্য প্রান্তের একটি থ্রেড ফলাফলটি গণনা করে এটি সংরক্ষণ করে std::promise। দ্রষ্টব্য: স্ট্যান্ডার্ডটির জন্য ভাগ করা রাষ্ট্র এবং এই নির্দিষ্ট ব্যবহারের ক্ষেত্রে std::futureকোনওটির অস্তিত্বের std::promiseপ্রয়োজন নেই।
ডেভিড রদ্রিগেজ - ড্রিবাস

6
@ কেরেকএসবি: থ্রেডে std::futureকল দেবে না join, এটির একটি ভাগ করা রাষ্ট্রের নির্দেশক রয়েছে যা প্রকৃত যোগাযোগ বাফার। ভাগ রাষ্ট্র একটি সমন্বয়সাধন প্রক্রিয়া (সম্ভবত হয়েছে std::function+ + std::condition_variableআহ্বানকারী লক না হওয়া পর্যন্ত std::promiseপূর্ণ করা হয়। থ্রেড সঞ্চালনের সব এই লম্ব হয়, এবং অনেক বাস্তবায়নের মাধ্যমে আপনি সূত্র খুঁজে পায় std::asyncনতুন থ্রেড যে তারপর যোগদান হয় দ্বারা সঞ্চালিত হয় না, কিন্তু পরিবর্তে একটি থ্রেড পুল দ্বারা যার আজীবন প্রোগ্রামের শেষ অবধি প্রসারিত।
ডেভিড রদ্রিগেজ - ড্রিবাস

1
@ ডেভিডরডগ্রিজেজ-ড্রিবিয়াস: মন্তব্যগুলি থেকে আপনার উত্তরটিতে তথ্য সম্পাদনা করুন।
মার্ক মুটজ - মিমুটজ

2
@ জোনাথন ওয়েকেলি: এর অর্থ এই নয় যে এটি একটি নতুন থ্রেডে মৃত্যুদন্ড কার্যকর করতে হবে, কেবল এটিই নয় যে এটি একটি নতুন তৈরি থ্রেডে চালিত হয়েছিল তেমনি অবিচ্ছিন্নভাবে কার্যকর করতে হবে । এর প্রধান সুবিধাটি std::asyncহ'ল রানটাইম লাইব্রেরিটি আপনার জন্য থ্রেডের সংখ্যা তৈরির বিষয়ে যথাযথ সিদ্ধান্ত নিতে পারে এবং বেশিরভাগ ক্ষেত্রে আমি থ্রেড পুল ব্যবহার করে রানটাইম আশা করি। বর্তমানে VS2012 ফণা অধীন একটি থ্রেড পুল ব্যবহার করে, এবং লঙ্ঘন করছে না তা যেমন-যদি নিয়ম। মনে রাখবেন যে খুব কম গ্যারান্টি রয়েছে যা এই বিশেষ হিসাবে যেমন পূরণ করা প্রয়োজন ।
ডেভিড রদ্রিগেজ - ড্রিবিস

1
থ্রেড স্থানীয়দের আবার নতুন করে শুরু করা দরকার, তবে বিধি-বিধানের ফলে কোনও কিছু অনুমতি দেয় (যার কারণেই আমি ইতালিগুলিতে "" যেমন "রেখেছি :)
জোনাথন ওয়েকেলি

11

std::promiseasync ফাংশন থেকে তথ্য ফেরত দেওয়ার জন্য চ্যানেল বা পথ way std::futureসিঙ্ক্রোনাইজেশন প্রক্রিয়াটি হ'ল কলটি প্রবাহিত ফেরতের মান std::promiseপ্রস্তুত না হওয়া পর্যন্ত অপেক্ষা করে তোলে (যার অর্থ কার্যটির ভিতরে এর মান সেট করা হয়)।


8

অ্যাসিনক্রোনাস প্রসেসিংয়ে সত্যই 3 টি মূল সত্ত্বা রয়েছে। সি ++ 11 বর্তমানে তাদের 2 টিতে ফোকাস করে।

অ্যাসিঙ্ক্রোনালিভাবে কিছু যুক্তি চালানোর জন্য আপনার প্রয়োজনীয় জিনিসগুলি হ'ল:

  1. কাজের (লজিক কিছু functor অবজেক্ট হিসেবে প্যাকেটজাত) যে 'কোথাও' চালানো হবে।
  2. প্রকৃত প্রক্রিয়াকরণ নোড - একটি থ্রেড, একটি প্রক্রিয়া, ইত্যাদি যে এই ধরনের functors চলন যখন তারা এটি প্রদান করা হয়। বেসিক কর্মী থ্রেড পুল কীভাবে এটি করে এটির একটি ভাল ধারণার জন্য "কমান্ড" নকশার প্যাটার্নটি দেখুন।
  3. ফলাফলের হাতল : যে কারো ফলাফলের প্রয়োজন, এবং একটি বস্তু এটি তাদের জন্য পাবেন প্রয়োজন। ওওপি এবং অন্যান্য কারণে, এই হ্যান্ডেলের API গুলিগুলিতে কোনও অপেক্ষার বা সিঙ্ক্রোনাইজেশন করা উচিত।

সি ++ 11 জিনিষ আমি (1) এর কথা বলতে আহ্বান std::promise, এবং (3) ঐ std::futurestd::thread(2) এর জন্য সর্বজনীনভাবে সরবরাহ করা একমাত্র জিনিস। এটি দুর্ভাগ্যজনক কারণ সত্যিকার প্রোগ্রামগুলি থ্রেড এবং মেমোরি রিসোর্সগুলি পরিচালনা করা দরকার এবং বেশিরভাগই ছোট ছোট কাজগুলির জন্য একটি থ্রেড তৈরি এবং ধ্বংস করার পরিবর্তে থ্রেড পুলগুলিতে চালিত হওয়া কাজ করতে চান (যা প্রায়শই নিজেরাই অপ্রয়োজনীয় পারফরম্যান্স হিট করে এবং সহজেই উত্স তৈরি করতে পারে) অনাহার যা আরও খারাপ)।

সি ++ 11 মস্তিষ্কের বিশ্বাসের std::executorহার্বসুটার এবং অন্যদের মতে, জাভা-র মতো একটি যুক্ত করার জন্য স্থায়ী পরিকল্পনা রয়েছে - থ্রেড পুল এবং যৌক্তিকভাবে অনুরূপ সেটআপগুলি (2) এর ভিত্তি হবে। আমরা এটি সি ++ ২০১৪ এ দেখতে পাব, তবে আমার বাজিটি আরও বেশি সি ++ ১ like এর মতো (এবং theseশ্বর যদি আমাদের এগুলিকে মান দেয় তবে আমাদের সাহায্য করুন)।


7

std::promiseএকটি প্রতিশ্রুতি / ভবিষ্যতের জুটির একটি শেষ পয়েন্ট হিসাবে তৈরি করা হয় এবং std::future( get_future()পদ্ধতিটি ব্যবহার করে স্ট্যান্ডার্ড :: প্রতিশ্রুতি তৈরি করা ) অন্য শেষ পয়েন্ট। এটি দুটি থ্রেডকে সিঙ্ক্রোনাইজ করার জন্য একটি উপায় প্রদানের একটি সহজ, একটি শট পদ্ধতি কারণ একটি থ্রেড একটি বার্তার মাধ্যমে অন্য থ্রেডে ডেটা সরবরাহ করে।

আপনি এটি ভাবতে পারেন যেহেতু একটি থ্রেড তথ্য সরবরাহ করার প্রতিশ্রুতি তৈরি করে এবং অন্য থ্রেড ভবিষ্যতে প্রতিশ্রুতি সংগ্রহ করে। এই প্রক্রিয়াটি কেবল একবার ব্যবহার করা যেতে পারে।

প্রতিশ্রুতি / ভবিষ্যতের প্রক্রিয়াটি কেবল একটি দিক, থ্রেড থেকে যা থ্রেডে এ set_value()পদ্ধতির std::promiseব্যবহার করে যা ডেটা পাওয়ার get()জন্য একটি ব্যবহার করে std::future। একটি ব্যতিক্রম উত্পন্ন হয় যদিget() ভবিষ্যতের পদ্ধতিটিকে একাধিকবার বলা হয় হয়।

যদি থ্রেডটি তার প্রতিশ্রুতিটি পূরণ std::promiseকরতে ব্যবহার না করে থাকে set_value()তবে যখন দ্বিতীয় থ্রেড প্রতিশ্রুতি সংগ্রহের আহ্বান জানায় get(), দ্বিতীয় থ্রেডটি যখন std::futureঅপেক্ষা করে তখন প্রথম থ্রেড দ্বারা পদ্ধতিটি std::promiseব্যবহার না করা অবধি পূর্ণ হয় না set_value()তথ্য প্রেরণ।

কারিগরী স্পেসিফিকেশন N4663 প্রোগ্রামিং ল্যাঙ্গুয়েজের প্রস্তাবিত কর্টিনগুলি সহ - Coroutines জন্য সি ++ এক্সটেনশন এবং ভিজ্যুয়াল স্টুডিও 2017 সি ++ সংকলক সমর্থন co_await, এটি ব্যবহার করা std::futureএবং std::asyncকর্টিন কার্যকারিতা লিখতেও সম্ভব। মধ্যে আলোচনা ও উদাহরণ দেখুন https://stackoverflow.com/a/50753040/1466970 যা এক অধ্যায় যে ব্যবহার সম্পর্কে আলোচনা করা যেমন রয়েছে std::futureসঙ্গে co_await

নিম্নলিখিত উদাহরণ কোড, একটি সাধারণ ভিজ্যুয়াল স্টুডিও 2013 উইন্ডোজ কনসোল অ্যাপ্লিকেশন, কয়েকটি সি ++ 11 সমাবর্তন শ্রেণি / টেমপ্লেট এবং অন্যান্য কার্যকারিতা ব্যবহার করে দেখায়। এটি প্রতিশ্রুতি / ভবিষ্যতের জন্য ব্যবহারের চিত্রিত করে যা ভালভাবে কাজ করে, স্বায়ত্তশাসিত থ্রেড যা কিছু কাজ করবে এবং থামবে, এবং এমন একটি ব্যবহার যেখানে আরও সুসংগত আচরণ প্রয়োজন এবং একাধিক বিজ্ঞপ্তির প্রয়োজনীয়তার কারণে প্রতিশ্রুতি / ভবিষ্যতের জুটি কাজ করে না।

এই উদাহরণ সম্পর্কে একটি নোট হ'ল বিভিন্ন স্থানে বিলম্ব। এই বিলম্বগুলি কেবলমাত্র কনসোলটিতে মুদ্রিত বিভিন্ন বার্তাগুলি std::coutপরিষ্কার হবে এবং বিভিন্ন থ্রেডের সেই পাঠ্যটি অন্তর্নির্মিত হবে না তা নিশ্চিত করার জন্য যুক্ত করা হয়েছিল ।

এর প্রথম অংশটি main()তিনটি অতিরিক্ত থ্রেড তৈরি করছে এবং ব্যবহার করে std::promiseএবং std::futureথ্রেডগুলির মধ্যে ডেটা প্রেরণ করছে। একটি আকর্ষণীয় বিষয় হ'ল মূল থ্রেডটি থ্রেড শুরু করে, টি 2, যা মূল থ্রেড থেকে ডেটার জন্য অপেক্ষা করবে, কিছু করবে এবং তারপরে তৃতীয় থ্রেড, টি 3-তে ডেটা প্রেরণ করবে, যা পরে কিছু করবে এবং তথ্যটিকে আবার পাঠাবে প্রধান থ্রেড.

main()মূল থ্রেড থেকে দুটি নির্মিত থ্রেডের প্রতিটিটিতে একাধিক বার্তা মঞ্জুরি দেওয়ার জন্য তৈরির দ্বিতীয় ভাগ দুটি থ্রেড এবং সারিগুলির একটি সেট তৈরি করে। আমরা ব্যবহার করতে পারি না std::promiseএবং এর std::futureজন্য কারণ প্রতিশ্রুতি / ভবিষ্যতের যুগল একটি শট এবং বারবার ব্যবহার করা যায় না।

শ্রেণীর উত্স Sync_queueহ'ল স্ট্রোস্ট্রপের সি সি ++ প্রোগ্রামিং ভাষা: ৪ র্থ সংস্করণ।

// cpp_threads.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <thread>  // std::thread is defined here
#include <future>  // std::future and std::promise defined here

#include <list>    // std::list which we use to build a message queue on.

static std::atomic<int> kount(1);       // this variable is used to provide an identifier for each thread started.

//------------------------------------------------
// create a simple queue to let us send notifications to some of our threads.
// a future and promise are one shot type of notifications.
// we use Sync_queue<> to have a queue between a producer thread and a consumer thread.
// this code taken from chapter 42 section 42.3.4
//   The C++ Programming Language, 4th Edition by Bjarne Stroustrup
//   copyright 2014 by Pearson Education, Inc.
template<typename Ttype>
class Sync_queue {
public:
    void  put(const Ttype &val);
    void  get(Ttype &val);

private:
    std::mutex mtx;                   // mutex used to synchronize queue access
    std::condition_variable cond;     // used for notifications when things are added to queue
    std::list <Ttype> q;              // list that is used as a message queue
};

template<typename Ttype>
void Sync_queue<Ttype>::put(const Ttype &val) {
    std::lock_guard <std::mutex> lck(mtx);
    q.push_back(val);
    cond.notify_one();
}

template<typename Ttype>
void Sync_queue<Ttype>::get(Ttype &val) {
    std::unique_lock<std::mutex> lck(mtx);
    cond.wait(lck, [this]{return  !q.empty(); });
    val = q.front();
    q.pop_front();
}
//------------------------------------------------


// thread function that starts up and gets its identifier and then
// waits for a promise to be filled by some other thread.
void func(std::promise<int> &jj) {
    int myId = std::atomic_fetch_add(&kount, 1);   // get my identifier
    std::future<int> intFuture(jj.get_future());
    auto ll = intFuture.get();   // wait for the promise attached to the future
    std::cout << "  func " << myId << " future " << ll << std::endl;
}

// function takes a promise from one thread and creates a value to provide as a promise to another thread.
void func2(std::promise<int> &jj, std::promise<int>&pp) {
    int myId = std::atomic_fetch_add(&kount, 1);   // get my identifier
    std::future<int> intFuture(jj.get_future());
    auto ll = intFuture.get();     // wait for the promise attached to the future

    auto promiseValue = ll * 100;   // create the value to provide as promised to the next thread in the chain
    pp.set_value(promiseValue);
    std::cout << "  func2 " << myId << " promised " << promiseValue << " ll was " << ll << std::endl;
}

// thread function that starts up and waits for a series of notifications for work to do.
void func3(Sync_queue<int> &q, int iBegin, int iEnd, int *pInts) {
    int myId = std::atomic_fetch_add(&kount, 1);

    int ll;
    q.get(ll);    // wait on a notification and when we get it, processes it.
    while (ll > 0) {
        std::cout << "  func3 " << myId << " start loop base " << ll << " " << iBegin << " to " << iEnd << std::endl;
        for (int i = iBegin; i < iEnd; i++) {
            pInts[i] = ll + i;
        }
        q.get(ll);  // we finished this job so now wait for the next one.
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::chrono::milliseconds myDur(1000);

    // create our various promise and future objects which we are going to use to synchronise our threads
    // create our three threads which are going to do some simple things.
    std::cout << "MAIN #1 - create our threads." << std::endl;

    // thread T1 is going to wait on a promised int
    std::promise<int> intPromiseT1;
    std::thread t1(func, std::ref(intPromiseT1));

    // thread T2 is going to wait on a promised int and then provide a promised int to thread T3
    std::promise<int> intPromiseT2;
    std::promise<int> intPromiseT3;

    std::thread t2(func2, std::ref(intPromiseT2), std::ref(intPromiseT3));

    // thread T3 is going to wait on a promised int and then provide a promised int to thread Main
    std::promise<int> intPromiseMain;
    std::thread t3(func2, std::ref(intPromiseT3), std::ref(intPromiseMain));

    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2 - provide the value for promise #1" << std::endl;
    intPromiseT1.set_value(22);

    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2.2 - provide the value for promise #2" << std::endl;
    std::this_thread::sleep_for(myDur);
    intPromiseT2.set_value(1001);
    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2.4 - set_value 1001 completed." << std::endl;

    std::future<int> intFutureMain(intPromiseMain.get_future());
    auto t3Promised = intFutureMain.get();
    std::cout << "MAIN #2.3 - intFutureMain.get() from T3. " << t3Promised << std::endl;

    t1.join();
    t2.join();
    t3.join();

    int iArray[100];

    Sync_queue<int> q1;    // notification queue for messages to thread t11
    Sync_queue<int> q2;    // notification queue for messages to thread t12

    std::thread t11(func3, std::ref(q1), 0, 5, iArray);     // start thread t11 with its queue and section of the array
    std::this_thread::sleep_for(myDur);
    std::thread t12(func3, std::ref(q2), 10, 15, iArray);   // start thread t12 with its queue and section of the array
    std::this_thread::sleep_for(myDur);

    // send a series of jobs to our threads by sending notification to each thread's queue.
    for (int i = 0; i < 5; i++) {
        std::cout << "MAIN #11 Loop to do array " << i << std::endl;
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
        q1.put(i + 100);
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
        q2.put(i + 1000);
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
    }

    // close down the job threads so that we can quit.
    q1.put(-1);    // indicate we are done with agreed upon out of range data value
    q2.put(-1);    // indicate we are done with agreed upon out of range data value

    t11.join();
    t12.join();
    return 0;
}

এই সাধারণ অ্যাপ্লিকেশনটি নিম্নলিখিত আউটপুট তৈরি করে।

MAIN #1 - create our threads.
MAIN #2 - provide the value for promise #1
  func 1 future 22
MAIN #2.2 - provide the value for promise #2
  func2 2 promised 100100 ll was 1001
  func2 3 promised 10010000 ll was 100100
MAIN #2.4 - set_value 1001 completed.
MAIN #2.3 - intFutureMain.get() from T3. 10010000
MAIN #11 Loop to do array 0
  func3 4 start loop base 100 0 to 5
  func3 5 start loop base 1000 10 to 15
MAIN #11 Loop to do array 1
  func3 4 start loop base 101 0 to 5
  func3 5 start loop base 1001 10 to 15
MAIN #11 Loop to do array 2
  func3 4 start loop base 102 0 to 5
  func3 5 start loop base 1002 10 to 15
MAIN #11 Loop to do array 3
  func3 4 start loop base 103 0 to 5
  func3 5 start loop base 1003 10 to 15
MAIN #11 Loop to do array 4
  func3 4 start loop base 104 0 to 5
  func3 5 start loop base 1004 10 to 15

1

প্রতিশ্রুতি তারের অন্য প্রান্ত হয়।

কল্পনা করুন যে আপনাকে একটি futureদ্বারা গুণমানের মান পুনরুদ্ধার করতে হবে async। তবে, আপনি চান না যে এটি একই থ্রেডে গণনা করা হবে, এবং আপনি একটি "থ্রেড" এখন পর্যন্ত থাবেন না - সম্ভবত আপনার সফ্টওয়্যারটি একটি পুল থেকে একটি সুতোর বাছাই করার জন্য তৈরি করা হয়েছিল, তাই আপনি জানেন না কে করবে শেষ পর্যন্ত চে গণনা করা।

এখন, আপনি এই (এখনও অজানা) থ্রেড / শ্রেণি / সত্তায় কী পাস করবেন? আপনি পাস করেন না future, কারণ এটি ফলাফল । আপনি এমন কিছুটি পাস করতে চান যা এর সাথে সংযুক্ত থাকে futureএবং এটি তারের অন্য প্রান্তকে উপস্থাপন করে , তাই আপনি futureকে আসলে কোনটি গণনা / লিখবেন সে সম্পর্কে কোনও জ্ঞান ছাড়াই প্রশ্নটি করবেন।

এই promise। এটা আপনার সাথে সংযুক্ত হ্যান্ডেলfuture । যদি futureএকটি হয় স্পিকার , এবং get()আপনি কি শুনছেন পর্যন্ত কিছু শব্দ আসে আউট শুরু, promiseএকটি হল মাইক্রোফোন ; কিন্তু ঠিক কোন মাইক্রোফোন, এটা মাইক্রোফোন স্পিকার তোমাকে ধরে রাখতে একটি তারের সঙ্গে সংযুক্ত। আপনি অন্য প্রান্তে কে তা জানেন তবে আপনার এটি জানা দরকার নেই - আপনি কেবল এটি দিন এবং অপর পক্ষের কিছু না বলা পর্যন্ত অপেক্ষা করুন।


0

http://www.cplusplus.com/reference/future/promise/

একটি বাক্য ব্যাখ্যা: furture :: get () promus :: set_value () চিরকাল অপেক্ষা করে।

void print_int(std::future<int>& fut) {
    int x = fut.get(); // future would wait prom.set_value forever
    std::cout << "value: " << x << '\n';
}

int main()
{
    std::promise<int> prom;                      // create promise

    std::future<int> fut = prom.get_future();    // engagement with future

    std::thread th1(print_int, std::ref(fut));  // send future to new thread

    prom.set_value(10);                         // fulfill promise
                                                 // (synchronizes with getting the future)
    th1.join();
    return 0;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.