यह कुछ काम करेगा।
सबसे पहले, task<Sig>
लिखें। task<Sig>
एक std::function
है जो केवल इसकी तर्क चलने योग्य, कॉपी करने योग्य नहीं है।
आपका आंतरिक प्रकारMessages
task<void()>
होने जा रहे हैं। तो आप आलसी हो सकते हैं और यदि आप चाहें तो task
केवल न्यूलरी फ़ंक्शंस का समर्थन करें।
दूसरा, भेजें std::packaged_task<R> package(f);
बनाता है। इसके बाद भविष्य को कार्य से बाहर कर दिया जाता है, और फिर संदेशों की अपनी कतार में package
चलाता है। (यही कारण है कि आपको केवल std::function
की आवश्यकता है, क्योंकि packaged_task
केवल स्थानांतरित किया जा सकता है)।
फिर आप packaged_task
से future
वापस लौटें।
template<class F, class R=std::result_of_t<F const&()>>
std::future<R> send(F&& f) {
packaged_task<R> package(std::forward<F>(f));
auto ret = package.get_future();
mq.push_back(std::move(package));
return ret;
}
ग्राहकों std::future
के ahold हड़पने और कॉल के पीछे परिणाम (बाद में) प्राप्त करने के लिए उपयोग कर सकते हैं।
template<class R>
struct task {
std::packaged_task<R> state;
template<class F>
task(F&& f):state(std::forward<F>(f)) {}
R operator()() const {
auto fut = state.get_future();
state();
return f.get();
}
};
लेकिन यह है कि (पैक काम उस में तुल्यकालन सामान है) हास्यास्पद अक्षम है, और शायद कुछ सफाई की जरूरत:
Amusingly, आप एक बहुत सरल कदम केवल-nullary कार्य इस प्रकार लिख सकते हैं। मुझे यह मनोरंजक लगता है क्योंकि यह केवल का उपयोग केवल std::function
भाग के लिए करता है।
व्यक्तिगत रूप से, मैंने केवल-केवल कार्य (इस समस्या के बीच) को महसूस करने के पर्याप्त कारणों में भाग लिया है ताकि यह महसूस किया जा सके कि केवल std::function
लेखन योग्य है। इस तरह के एक कार्यान्वयन का पालन करता है। यह भारी अनुकूलित है (शायद के रूप में तेजी से के बारे में अधिकांश के रूप में std::function
हालांकि), और डिबग नहीं, लेकिन डिजाइन आवाज़ है:
template<class Sig>
struct task;
namespace details_task {
template<class Sig>
struct ipimpl;
template<class R, class...Args>
struct ipimpl<R(Args...)> {
virtual ~ipimpl() {}
virtual R invoke(Args&&...args) const = 0;
};
template<class Sig, class F>
struct pimpl;
template<class R, class...Args, class F>
struct pimpl<R(Args...), F>:ipimpl<R(Args...)> {
F f;
R invoke(Args&&...args) const final override {
return f(std::forward<Args>(args)...);
};
};
// void case, we don't care about what f returns:
template<class...Args, class F>
struct pimpl<void(Args...), F>:ipimpl<void(Args...)> {
F f;
template<class Fin>
pimpl(Fin&&fin):f(std::forward<Fin>(fin)){}
void invoke(Args&&...args) const final override {
f(std::forward<Args>(args)...);
};
};
}
template<class R, class...Args>
struct task<R(Args...)> {
std::unique_ptr< details_task::ipimpl<R(Args...)> > pimpl;
task(task&&)=default;
task&operator=(task&&)=default;
task()=default;
explicit operator bool() const { return static_cast<bool>(pimpl); }
R operator()(Args...args) const {
return pimpl->invoke(std::forward<Args>(args)...);
}
// if we can be called with the signature, use this:
template<class F, class=std::enable_if_t<
std::is_convertible<std::result_of_t<F const&(Args...)>,R>{}
>>
task(F&& f):task(std::forward<F>(f), std::is_convertible<F&,bool>{}) {}
// the case where we are a void return type, we don't
// care what the return type of F is, just that we can call it:
template<class F, class R2=R, class=std::result_of_t<F const&(Args...)>,
class=std::enable_if_t<std::is_same<R2, void>{}>
>
task(F&& f):task(std::forward<F>(f), std::is_convertible<F&,bool>{}) {}
// this helps with overload resolution in some cases:
task(R(*pf)(Args...)):task(pf, std::true_type{}) {}
// = nullptr support:
task(std::nullptr_t):task() {}
private:
// build a pimpl from F. All ctors get here, or to task() eventually:
template<class F>
task(F&& f, std::false_type /* needs a test? No! */):
pimpl(new details_task::pimpl<R(Args...), std::decay_t<F>>{ std::forward<F>(f) })
{}
// cast incoming to bool, if it works, construct, otherwise
// we should be empty:
// move-constructs, because we need to run-time dispatch between two ctors.
// if we pass the test, dispatch to task(?, false_type) (no test needed)
// if we fail the test, dispatch to task() (empty task).
template<class F>
task(F&& f, std::true_type /* needs a test? Yes! */):
task(f?task(std::forward<F>(f), std::false_type{}):task())
{}
};
live example।
लाइब्रेरी-क्लास मूव-केवल कार्य ऑब्जेक्ट में पहला स्केच है। यह कुछ सी ++ 14 सामान (std::blah_t
उपनाम) का भी उपयोग करता है - typename std::enable_if<???>::type
के साथ प्रतिस्थापित करें यदि आप सी ++ 11-केवल कंपाइलर हैं।
ध्यान दें कि void
रिटर्न टाइप चाल में कुछ मामूली संदिग्ध टेम्पलेट अधिभार चाल शामिल हैं। (यह तर्कसंगत है कि यह मानक के शब्दों के तहत कानूनी है, लेकिन प्रत्येक सी ++ 11 कंपाइलर इसे स्वीकार करेगा, और यदि यह नहीं है तो यह कानूनी बनने की संभावना है)।
क्या आप (अनदेखा) std :: async के लिए पूछ रहे हैं? – stefan
यदि मैं संदर्भ सही पढ़ता हूं, तो 'std :: async' नए धागे लॉन्च करता है या थ्रेड पूल का उपयोग करता है। मैं क्रमिक निष्पादन के लिए एक थ्रेड पर कार्यों को कतार करना चाहता हूं। – enkelwor