2011-07-11 18 views
5

एक बड़े प्रोग्राम का निम्न न्यूनतम कोड नमूना क्लाइंट थ्रेड से कमांड को एक asio io_service ऑब्जेक्ट में भेजता है। Io_service ऑब्जेक्ट (आईओएस कक्षा में) एक थ्रेड के साथ चलाया जा रहा है। जब आदेश भेजा जाता है तो क्लाइंट थ्रेड तब तक प्रतीक्षा करता है जब तक इसे आईओएस ऑब्जेक्ट (सीएमडी :: नोटिफ़ाईफिशिश() के माध्यम से अधिसूचित किया जाता है) द्वारा अधिसूचित किया जाता है।बूस्ट कंडीशन वेरिएबल इश्यू

यह नमूना लिनक्स उबंटू 11.04 पर 1.46 जुर्माना के साथ चल रहा है लेकिन विंडोज 7 पर 1.46 पर जोर देता है।

मुझे संदेह है कि यह सीएमडी :: NotifyFinish() में लॉक के साथ कुछ करना है। जब मैं नेस्टेड स्कोप से लॉक को स्थानांतरित करता हूं ताकि जब प्रतीक्षा करें कैंडीशन Variable_.notify_one() को लॉक के दायरे में बुलाया जाता है तो यह विंडोज 7 पर क्रैश नहीं होता है। हालांकि, बूस्ट :: थ्रेड प्रलेखन में कहा गया है कि notify_one() की आवश्यकता नहीं है लॉक के भीतर बुलाया जाना है।

स्टैक ट्रेस (नीचे) दिखाता है कि यह notify_one() कहा जाता है जब यह जोर दे रहा है। ऐसा लगता है कि सूचित करने से पहले cmd ऑब्जेक्ट गायब हो गया है ...

मैं इस थ्रेड को सुरक्षित कैसे बना सकता हूं और जोर नहीं दे सकता?

#include <boost/asio.hpp> 
#include <boost/thread/thread.hpp> 
#include <boost/thread/mutex.hpp> 
#include <boost/thread/locks.hpp> 
#include <boost/thread/condition_variable.hpp> 
#include <boost/bind.hpp> 
#include <iostream> 

class Cmd 
{ 
public: 
    Cmd() : cnt_(0), waitPred_(false), waiting_(false) 
    { 
    } 
    virtual ~Cmd() 
    { 
    } 
    void BindInfo(int CmdSeq) 
    { 
     cnt_ = CmdSeq; 
    } 
    void NotifyFinish() 
    { 
     // call by service thread... 
     { 
      boost::mutex::scoped_lock lock(waitMutex_); 
      waitPred_ = true; 
      if (!waiting_) 
      { 
       // don't need to notify as isn't waiting 
       return; 
      } 
     } 
     waitConditionVariable_.notify_one(); 
    } 
    void Wait() 
    { 
     // called by worker threads 
     boost::mutex::scoped_lock lock(waitMutex_); 
     waiting_ = true; 
     while (!waitPred_) 
      waitConditionVariable_.wait(lock); 
    } 
    int cnt_; 
private: 

    boost::mutex waitMutex_; 
    boost::condition_variable waitConditionVariable_; 
    bool waitPred_; 
    bool waiting_; 
}; 


class Ios 
{ 
public: 
    Ios() : timer_(ios_), cnt_(0), thread_(boost::bind(&Ios::Start, this)) 
    { 
    } 
    void Start() 
    { 
     timer_.expires_from_now(boost::posix_time::seconds(5)); 
     timer_.async_wait(boost::bind(&Ios::TimerHandler, this, _1)); 
     ios_.run(); 
    } 
    void RunCmd(Cmd& C) 
    { 
     ios_.post(boost::bind(&Ios::RunCmdAsyn, this, boost::ref(C))); 
    } 

private: 
    void RunCmdAsyn(Cmd& C) 
    { 
     C.BindInfo(cnt_++); 
     C.NotifyFinish(); 
    } 
    void TimerHandler(const boost::system::error_code& Ec) 
    { 
     if (!Ec) 
     { 
      std::cout << cnt_ << "\n"; 
      timer_.expires_from_now(boost::posix_time::seconds(5)); 
      timer_.async_wait(boost::bind(&Ios::TimerHandler, this, _1)); 
     } 
     else 
      exit(0); 
    } 

    boost::asio::io_service ios_; 
    boost::asio::deadline_timer timer_; 
    int cnt_; 
    boost::thread thread_; 
}; 

static Ios ios; 

void ThreadFn() 
{ 
    while (1) 
    { 
     Cmd c; 
     ios.RunCmd(c); 
     c.Wait(); 
     //std::cout << c.cnt_ << "\n"; 
    } 
} 

int main() 
{ 
    std::cout << "Starting\n"; 
    boost::thread_group threads; 
    const int num = 5; 

    for (int i = 0; i < num; i++) 
    { 
     // Worker threads 
     threads.create_thread(ThreadFn); 
    } 
    threads.join_all(); 

} 

स्टैक ट्रेस

msvcp100d.dll!std::_Debug_message(const wchar_t * message, const wchar_t * file, unsigned int line) Line 15 C++ 
iosthread.exe!std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >::_Compat(const std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > & _Right) Line 238 + 0x17 bytes C++ 
iosthread.exe!std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >::operator==(const std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > & _Right) Line 203 C++ 
iosthread.exe!std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >::operator!=(const std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > & _Right) Line 208 + 0xc bytes C++ 
iosthread.exe!std::_Debug_range2<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > >(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, const wchar_t * _File, unsigned int _Line, std::random_access_iterator_tag __formal) Line 715 + 0xc bytes C++ 
iosthread.exe!std::_Debug_range<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > >(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, const wchar_t * _File, unsigned int _Line) Line 728 + 0x6c bytes C++ 
iosthread.exe!std::find_if<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >,bool (__cdecl*)(boost::intrusive_ptr<boost::detail::basic_cv_list_entry> const &)>(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, bool (const boost::intrusive_ptr<boost::detail::basic_cv_list_entry> &)* _Pred) Line 92 + 0x54 bytes C++ 
iosthread.exe!std::remove_if<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >,bool (__cdecl*)(boost::intrusive_ptr<boost::detail::basic_cv_list_entry> const &)>(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, bool (const boost::intrusive_ptr<boost::detail::basic_cv_list_entry> &)* _Pred) Line 1848 + 0x58 bytes C++ 
iosthread.exe!boost::detail::basic_condition_variable::notify_one() Line 267 + 0xb4 bytes C++ 
iosthread.exe!Cmd::NotifyFinish() Line 41 C++ 
+0

प्रतिकृति –

उत्तर

5

समस्या यह है कि हालत चर Cmd उद्देश्य यह है कि ग्राहक धागा द्वारा बनाई गई है और जब इंतजार है कि ग्राहक धागा द्वारा नष्ट हो जाता है का एक सदस्य है है पूरा कर लिया है।

  • boost::condition_variable::notify_one() 'सेवा धागा'
  • है कि ग्राहक धागा है कि उस हालत चर
  • ग्राहक धागा तो नष्ट कर सकते हैं पर इंतजार कर रहा है को खोलता है पर कहा जाता है:

    तो तुम एक रेस स्थिति जहां है हालत परिवर्तनीय है कि सर्विस थ्रेड अभी भी notify_one पर कॉल के साथ काम कर रहा है।

तो अपने अवलोकन है कि यह "के रूप में यद्यपि cmd वस्तु से पहले सूचित कहा जाता है गायब हो गया है" काफी वास्तव में क्या हुआ है, मुझे लगता है। सिवाय इसके कि Cmd ऑब्जेक्ट notify_one() से पहले गायब नहीं हुआ, यह गायब हो गया, जबकि notify_one() अपना काम कर रहा था। आपका अन्य नोट है कि "boost::thread दस्तावेज़ीकरण बताता है कि notify_one() को लॉक के भीतर बुलाया जाने की आवश्यकता नहीं है", लेकिन इसका मतलब यह नहीं है कि notify_one() से पहले स्थिति चर को नष्ट किया जा सकता है।

आप इतना है कि सेवा धागा यह प्रयोग किया जाता है Cmd वस्तु के जीवनकाल का प्रबंधन करने की जरूरत है यह पहले हो जाता है को नष्ट कर दिया - म्युटेक्स Cmd वस्तु में है कि पकड़े notify_one() कहा जाता है एक ही रास्ता है कि ऐसा करने के लिए है (आप के रूप में ' मैंने देखा)। या आप Cmd ऑब्जेक्ट से कंडीशन वैरिएबल को खींच सकते हैं ताकि उसका जीवनकाल Cmd ऑब्जेक्ट से स्वतंत्र हो (शायद shared_ptr<> उसमें सहायता कर सके)।

ही, ध्यान दें मुझे विश्वास है कि कि Cmd वर्ग के waiting_ सदस्य ज़रूरत से ज़्यादा है - आप notify_one() या notify_all() कॉल कर सकते हैं जब वहाँ एक शर्त चर पर कोई वेटर कर रहे हैं - यह पहले से ही (मैं डॉन 'है कि आप के लिए के लिए जाँच कर रहा है टी लगता है कि यह कुछ भी नुकसान पहुंचा रहा है, बस यह जटिलता है कि Cmd कक्षा में होने की आवश्यकता नहीं है)।

+0

के लिए +1 आपके उत्तर माइकल के लिए धन्यवाद। WaitifyFinish() में लॉक को बाहरी स्कोप बल में नहीं ले जायेगा Notify_one() प्रतीक्षा() से बाहर जाने से पहले कॉल किया जा सकता है, क्योंकि waitConditionVariable_.wait खत्म होने के बाद लॉक प्राप्त करता है? तो दो परिदृश्य हैं जो प्रतीक्षा कर सकते हैं। या तो जब अधिसूचना लॉक प्राप्त करती है, तो प्रतीक्षा करें या प्रतीक्षा करें जब पहले लॉक प्राप्त होता है और फिर स्थिति चर को अधिसूचित किया जाता है। मैं मानता हूं कि प्रतीक्षा_ स्मृति अधूरा लगता है। – Liam

+0

@ लीम: यह मानते हुए कि परिवर्तन किया गया है, इसलिए 'NotifyFinish() 'म्यूटेक्स रखता है, जबकि यह' notify_one() 'कहता है, फिर आपके पहले परिदृश्य में जहां' NotifyFinish() 'पहले कॉल किया जाता है, यह mutex को पकड़ लेगा , 'waitPred_ = true' सेट करें, फिर' notify_one() 'कॉल करें (जो कुछ भी नहीं करेगा)। इससे पहले 'प्रतीक्षा करें') कुछ भी करने का मौका मिलने से पहले होगा, क्योंकि अगर यह कोशिश करता है तो इसे म्यूटेक्स प्राप्त करने के लिए अवरुद्ध कर दिया जाएगा। एक बार 'NotifyFinish()' mutex को रिलीज़ करने के बाद, 'प्रतीक्षा करें()' कॉल आगे बढ़ने में सक्षम हो जाएगा; यह ध्यान देगा कि 'waitPred_'' सत्य 'है और' condition_variable :: प्रतीक्षा() 'को कॉल करने से परेशान नहीं होगा। –

1
void ThreadFn() 
{ 
    while (1) 
    { 
     Cmd c; 
     ios.RunCmd(c); 
     c.Wait(); 
     //std::cout << c.cnt_ << "\n"; 
    } 
} 

चूंकि यह लूप अनंत है क्यों न केवल सीएमडी सी डालें; थोड़ी देर के दायरे के बाहर (1) तो यह (1) के प्रत्येक पुनरावृत्ति 'सी' का पुन: उपयोग करता है?

void ThreadFn() 
{ 
    Cmd c; 

    while (1) 
    { 
     ios.RunCmd(c); 
     c.Wait(); 
     //std::cout << c.cnt_ << "\n"; 
    } 
} 
+0

कोड समस्या का प्रदर्शन करने के लिए केवल एक बड़े कार्यक्रम का एक extrapolation था। कन्स्ट्रक्टर में कई प्रकार के कमांड शुरू किए गए थे, और वहां कुछ भी नहीं था (1) लूप। – Liam

संबंधित मुद्दे