Packaged_task और async के बीच क्या अंतर है


134

C ++ 11 के थ्रेडेड मॉडल के साथ काम करते हुए, मैंने देखा कि

std::packaged_task<int(int,int)> task([](int a, int b) { return a + b; });
auto f = task.get_future();
task(2,3);
std::cout << f.get() << '\n';

तथा

auto f = std::async(std::launch::async, 
    [](int a, int b) { return a + b; }, 2, 3);
std::cout << f.get() << '\n';

लगता है बिल्कुल वही काम करते हैं। मैं समझता हूं कि अगर मैं std::asyncसाथ चलता तो एक बड़ा अंतर हो सकता है std::launch::deferred, लेकिन क्या इस मामले में कोई है?

इन दो दृष्टिकोणों के बीच अंतर क्या है, और इससे भी महत्वपूर्ण बात यह है कि मुझे एक के बाद एक किन मामलों में उपयोग करना चाहिए?

जवाबों:


161

वास्तव में आपने जो उदाहरण दिया है वह अंतर दिखाता है यदि आप एक लंबे समय के फ़ंक्शन का उपयोग करते हैं, जैसे कि

//! sleeps for one second and returns 1
auto sleep = [](){
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 1;
};

पैक किए गए कार्य

A packaged_taskइसकी शुरुआत खुद नहीं करेगा, आपको इसे लागू करना होगा:

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

दूसरी ओर, std::asyncके साथ launch::asyncएक अलग थ्रेड में काम चलाने का प्रयास करेंगे:

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";

कमी

लेकिन इससे पहले कि आप asyncसब कुछ के लिए उपयोग करने का प्रयास करें , ध्यान रखें कि लौटे भविष्य में एक विशेष साझा स्थिति है, जो उस future::~futureब्लॉक की मांग करती है:

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;
*/

इसलिए यदि आप वास्तविक अतुल्यकालिक चाहते हैं तो आपको लौटाए रखने की आवश्यकता है future, या यदि आप परिस्थितियों को बदलते हैं तो परिणाम की परवाह नहीं करते हैं:

{
    auto pizza = std::async(get_pizza);
    /* ... */
    if(need_to_go)
        return;          // ~future will block
    else
       eat(pizza.get());
}   

इस बारे में अधिक जानकारी के लिए, हर्ब सुतर की आलेख देखें asyncऔर~future है, जो समस्या का वर्णन करता है, और स्कॉट मेयर की std::futuresसे std::asyncविशेष नहीं कर रहे हैं , जो अंतर्दृष्टि वर्णन करता है। यह भी ध्यान दें कि यह व्यवहार C ++ 14 और ऊपर निर्दिष्ट किया गया था , लेकिन सामान्यतः C ++ 11 में भी लागू किया गया है।

आगे मतभेद

उपयोग करने से std::asyncआप अपने कार्य को एक विशेष धागे पर नहीं चला सकते हैं, जहां std::packaged_taskअन्य धागे में स्थानांतरित किया जा सकता है।

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";

इसके अलावा, packaged_taskकॉल करने से पहले आपको इनवाइट करना होगा f.get(), अन्यथा आप प्रोग्राम को फ्रीज कर देंगे क्योंकि भविष्य कभी तैयार नहीं होगा:

std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);

टी एल; डॉ

std::asyncयदि आप कुछ काम करना चाहते हैं तो उपयोग करें और जब वे कर रहे हों तो वास्तव में परवाह न करें, और std::packaged_taskयदि आप चीजों को अन्य थ्रेड्स में स्थानांतरित करने के लिए या बाद में उन्हें कॉल करना चाहते हैं। या, ईसाई को उद्धृत करने के लिए :

अंत में ए std::packaged_taskलागू करने के लिए सिर्फ एक निचले स्तर की विशेषता है std::async(यही कारण है कि यह std::asyncअन्य निचले स्तर के सामान, जैसे कि एक साथ उपयोग किए जाने से अधिक कर सकता है std::thread)। सीधे शब्दों में बोले गए एक std::packaged_taskएक है std::functionएक से जुड़ा हुआ std::futureहै और std::asyncwraps और एक कॉल std::packaged_task(संभवतः एक अलग धागे में)।


9
आपको जोड़ना चाहिए कि भविष्य में विनाश पर async ब्लॉक द्वारा लौटाया गया है (जैसे कि अगर आपको फोन मिलता है), जबकि पैक किए गए से वापस लौटा_तक नहीं होता है।
जॉन ५३४२

22
अंत में ए std::packaged_taskलागू करने के लिए सिर्फ एक निचले स्तर की विशेषता है std::async(यही कारण है कि यह std::asyncअन्य निचले स्तर के सामान, जैसे कि एक साथ उपयोग किए जाने से अधिक कर सकता है std::thread)। सीधे शब्दों में बोले गए एक std::packaged_taskएक है std::functionएक से जुड़ा हुआ std::futureहै और std::asyncwraps और एक कॉल std::packaged_task(संभवतः एक अलग धागे में)।
क्रिश्चियन राऊ

मैं ~ भविष्य () ब्लॉक पर कुछ प्रयोग कर रहा हूं। मैं भविष्य की वस्तु विनाश पर अवरुद्ध प्रभाव को दोहरा नहीं सकता था। सब कुछ अतुल्यकालिक रूप से काम किया। मैं वीएस 2013 का उपयोग कर रहा हूं और जब मैं एसिंक्स लॉन्च करता हूं, तो मैंने एसडी :: लॉन्च :: एसिंक्स का इस्तेमाल किया। क्या वीसी ++ किसी तरह इस मुद्दे को "ठीक" करता है?
फ्रैंक लियू

1
@FrankLiu: खैर, N3451 एक स्वीकृत प्रस्ताव है, जो (जहां तक ​​मुझे पता है) C ++ 14 में चला गया। यह देखते हुए कि हर्ब Microsoft में काम करता है, मुझे आश्चर्य नहीं होगा कि क्या यह सुविधा VS2013 में लागू है। एक कंपाइलर जो C ++ 11 नियमों का सख्ती से पालन करता है, फिर भी यह व्यवहार दिखाएगा।
जीटा

1
@ मिखाइल यह उत्तर C ++ 14 और C ++ 17 दोनों से पहले है, इसलिए मेरे पास मानक नहीं थे, लेकिन केवल प्रस्ताव थे। मैं पैराग्राफ निकाल दूंगा।
ज़ीटा

1

पैक टास्क बनाम async

p> पैकेज्ड टास्क में एक टास्क[function or function object]और फ्यूचर / वादा पेयर होता है। काम के लिए एक वापसी कथन निष्पादित करता है, यह कारण बनता हैset_value(..)परpackaged_taskकी वादा।

a> भविष्य को देखते हुए, वादा और पैकेज कार्य हम थ्रेड्स के बारे में बहुत ज्यादा चिंता किए बिना सरल कार्य बना सकते हैं [धागा केवल एक चीज है जिसे हम एक कार्य चलाने के लिए देते हैं]।

हालाँकि हमें इस बात पर विचार करने की आवश्यकता है कि कितने धागे का उपयोग करना है या क्या कोई कार्य वर्तमान थ्रेड पर या किसी अन्य पर सबसे अच्छा चलाया जाता है। किसी डिस्चार्ज को थ्रेड लांचर द्वारा नियंत्रित किया जा सकता है async(), जो यह तय करता है कि नया थ्रेड बनाना है या पुराने को रीसायकल करना है एक या बस कार्य को वर्तमान थ्रेड पर चलाएँ। यह एक भविष्य देता है।


0

"क्लास टेम्प्लेट std :: packaged_task किसी भी कॉल करने योग्य लक्ष्य (फ़ंक्शन, लैम्ब्डा एक्सप्रेशन, बाइंड एक्सप्रेशन या किसी अन्य फ़ंक्शन ऑब्जेक्ट) को लपेटता है ताकि इसे एसिंक्रोनस रूप से आमंत्रित किया जा सके। इसका रिटर्न वैल्यू या अपवाद फेंका एक साझा स्थिति में संग्रहीत है जिसे एक्सेस किया जा सकता है। एसटीडी के माध्यम से :: भविष्य की वस्तुएं। "

"टेम्पलेट फ़ंक्शन async फ़ंक्शन को असिंक्रोनस रूप से चलाता है (संभवतः एक अलग थ्रेड में) और एक std :: future देता है जो अंततः उस फ़ंक्शन कॉल के परिणाम को धारण करेगा।"

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.