2011-02-16 16 views
6

मुझे pthreads में समस्या है जहां मुझे लगता है कि मुझे डेडलॉक मिल रहा है। मैंने एक अवरुद्ध कतार बनाई है जिसे मैंने सोचा था कि काम कर रहा था, लेकिन कुछ और परीक्षण करने के बाद मुझे पता चला है कि अगर मैं ब्लॉकिंग_क्यू पर अवरुद्ध कर रहे कई धागे को आजमाता हूं और रद्द करता हूं, तो मुझे लगता है कि मुझे डेडलॉक मिल रहा है।सी ++ पर्थ्रेड अवरुद्ध कतार डेडलॉक (मुझे लगता है)

अवरुद्ध कतार बहुत सरल है और इस तरह दिखता है:

template <class T> class Blocking_Queue 
{ 
public: 
    Blocking_Queue() 
    { 
     pthread_mutex_init(&_lock, NULL); 
     pthread_cond_init(&_cond, NULL); 
    } 

    ~Blocking_Queue() 
    { 
     pthread_mutex_destroy(&_lock); 
     pthread_cond_destroy(&_cond); 
    } 

    void put(T t) 
    { 
     pthread_mutex_lock(&_lock); 
     _queue.push(t); 
     pthread_cond_signal(&_cond); 
     pthread_mutex_unlock(&_lock); 
    } 

    T pull() 
    { 
     pthread_mutex_lock(&_lock); 
     while(_queue.empty()) 
     { 
      pthread_cond_wait(&_cond, &_lock); 
     } 

     T t = _queue.front(); 
     _queue.pop(); 

     pthread_mutex_unlock(&_lock); 

     return t; 
    } 

priavte: 
    std::queue<T> _queue; 
    pthread_cond_t _cond; 
    pthread_mutex_t _lock; 
} 

परीक्षण के लिए, मैं 4 धागे कि इस अवरोधन कतार पर खींच बनाया है। मैंने अवरुद्ध कतार में कुछ प्रिंट स्टेटमेंट जोड़े और प्रत्येक थ्रेड pthread_cond_wait() विधि पर जा रहा है। हालांकि, जब मैं प्रत्येक थ्रेड पर pthread_cancel() और pthread_join() को कॉल करने का प्रयास करता हूं तो प्रोग्राम बस लटकता है।

मैंने इसे केवल एक धागे से भी परीक्षण किया है और यह पूरी तरह से काम करता है।

प्रलेखन के अनुसार, pthread_cond_wait() रद्दीकरण बिंदु है, इसलिए उन थ्रेडों पर रद्द करने से उन्हें निष्पादन को रोकना चाहिए (और यह केवल 1 धागे के साथ काम करता है)। हालांकि pthread_mutex_lock रद्द करने का बिंदु नहीं है। जब pthread_cancel() कहा जाता है, तो कुछ रद्द हो सकता है, रद्द किए गए थ्रेड को समाप्त करने से पहले म्यूटेक्स प्राप्त होता है और इसे अनलॉक नहीं करता है, और फिर जब अगला थ्रेड रद्द हो जाता है तो यह म्यूटेक्स और डेडलॉक्स को प्राप्त नहीं कर सकता है? या क्या कुछ और है जो मैं गलत कर रहा हूं।

कोई सलाह सुंदर होगी। धन्यवाद :)

+0

कोशिश का उपयोग कर [helgrind] (http://valgrind.org/info/tools.html#helgrind), यह उपयोगी अतीत में मेरे लिए किया गया है दौड़ की स्थिति और deadlocks तलाशने के लिए। – Flexo

+0

रद्दीकरण विश्वासघाती हो सकता है। कृपया हमें अपना अधिक तर्क दिखाएं: आपके कार्यकर्ता धागे की रद्द करने की स्थिति क्या है?क्या सफाई हैंडलर? और आप कई धागे को रद्द/शामिल करने के लिए कॉल कैसे अनुक्रमित कर रहे हैं? – pilcrow

उत्तर

5

pthread_cancel() सबसे अच्छा बचा है।

आप वहां से अपवाद फेंककर Blocking_Queue :: pull() पर अपने सभी थ्रेड को अवरुद्ध कर सकते हैं।

कतार में एक कमजोर जगह यह है कि T t = _queue.front(); टी के प्रतिलिपि निर्माता को आमंत्रित करता है जो एक अपवाद फेंक सकता है, जो आपको क्यूई म्यूटेक्स को हमेशा के लिए बंद कर देता है। सी ++ स्कॉप्ड ताले का बेहतर उपयोग करें।

यहाँ सुंदर धागा समाप्ति का एक उदाहरण है:

$ cat test.cc 
#include <boost/thread/mutex.hpp> 
#include <boost/thread/thread.hpp> 
#include <boost/thread/condition_variable.hpp> 
#include <exception> 
#include <list> 
#include <stdio.h> 

struct BlockingQueueTerminate 
    : std::exception 
{}; 

template<class T> 
class BlockingQueue 
{ 
private: 
    boost::mutex mtx_; 
    boost::condition_variable cnd_; 
    std::list<T> q_; 
    unsigned blocked_; 
    bool stop_; 

public: 
    BlockingQueue() 
     : blocked_() 
     , stop_() 
    {} 

    ~BlockingQueue() 
    { 
     this->stop(true); 
    } 

    void stop(bool wait) 
    { 
     // tell threads blocked on BlockingQueue::pull() to leave 
     boost::mutex::scoped_lock lock(mtx_); 
     stop_ = true; 
     cnd_.notify_all(); 

     if(wait) // wait till all threads blocked on the queue leave BlockingQueue::pull() 
      while(blocked_) 
       cnd_.wait(lock); 
    } 

    void put(T t) 
    { 
     boost::mutex::scoped_lock lock(mtx_); 
     q_.push_back(t); 
     cnd_.notify_one(); 
    } 

    T pull() 
    { 
     boost::mutex::scoped_lock lock(mtx_); 

     ++blocked_; 
     while(!stop_ && q_.empty()) 
      cnd_.wait(lock); 
     --blocked_; 

     if(stop_) { 
      cnd_.notify_all(); // tell stop() this thread has left 
      throw BlockingQueueTerminate(); 
     } 

     T front = q_.front(); 
     q_.pop_front(); 
     return front; 
    } 
}; 

void sleep_ms(unsigned ms) 
{ 
    // i am using old boost 
    boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(ms)); 
    // with latest one you can do this 
    //boost::thread::sleep(boost::posix_time::milliseconds(10)); 
} 

void thread(int n, BlockingQueue<int>* q) 
try 
{ 
    for(;;) { 
     int m = q->pull(); 
     printf("thread %u: pulled %d\n", n, m); 
     sleep_ms(10); 
    } 
} 
catch(BlockingQueueTerminate&) 
{ 
    printf("thread %u: finished\n", n); 
} 

int main() 
{ 
    BlockingQueue<int> q; 

    // create two threads 
    boost::thread_group tg; 
    tg.create_thread(boost::bind(thread, 1, &q)); 
    tg.create_thread(boost::bind(thread, 2, &q)); 
    for(int i = 1; i < 10; ++i) 
     q.put(i); 
    sleep_ms(100); // let the threads do something 
    q.stop(false); // tell the threads to stop 
    tg.join_all(); // wait till they stop 
} 

$ g++ -pthread -Wall -Wextra -o test -lboost_thread-mt test.cc 

$ ./test 
thread 2: pulled 1 
thread 1: pulled 2 
thread 1: pulled 3 
thread 2: pulled 4 
thread 1: pulled 5 
thread 2: pulled 6 
thread 1: pulled 7 
thread 2: pulled 8 
thread 1: pulled 9 
thread 2: finished 
thread 1: finished 
+0

यह मेरी समस्या का एक बहुत अच्छा समाधान है, इसे साझा करने के लिए धन्यवाद! :) – vimalloc

1

मैं pthread_cancel() से बिल्कुल परिचित नहीं हूं - मैं सहकारी समाप्ति पसंद करता हूं।

क्या कोई pthread_cancel() आपके म्यूटेक्स को लॉक नहीं करेगा? मुझे लगता है कि आपको रद्दीकरण हैंडलर के साथ सफाई करने की आवश्यकता है।

+0

सिद्धांत में जब pthread_cond_wait() कहा जाता है, mutex जारी किया जाना चाहिए (और ऐसा इसलिए है, क्योंकि एकाधिक धागे इसे pthread_cond_wait() कथन में बनाते हैं)। हालांकि जब pthread_cancel कहा जा रहा है यह म्यूटेक्स की तरह दिख रहा है, कम से कम यह मेरे लिए मेरी व्याख्या है। – vimalloc

+1

म्यूटेक्स shalll वास्तव में पुनः प्राप्त किया जाएगा (अगर pthread_cond_wait() रद्द कर दिया गया है)। Http://pubs.opengroup.org/onlinepubs/007908799/xsh/pthread_cond_wait.html देखें "एक शर्त प्रतीक्षा (चाहे समय या नहीं) रद्दीकरण बिंदु है। जब रद्द करने योग्यता सक्षम करता है तो थ्रेड की स्थिति PTHREAD_CANCEL_DEFERRED पर सेट होती है, एक रद्दीकरण अनुरोध पर एक रद्दीकरण अनुरोध पर कार्य करने का दुष्प्रभाव यह है कि म्यूटेक्स (प्रभाव में) पहले रद्दीकरण क्लीनअप हैंडलर को कॉल करने से पहले पुनः अधिग्रहण किया जाता है। " – sstn

1

मुझे pthread_cond_wait()/pthread_cancel() के साथ समान अनुभव मिला है। किसी भी कारण से धागे लौटने के बाद भी मुझे लॉक के साथ समस्याएं थीं, और इसे अनलॉक करना असंभव था, क्योंकि आपको लॉक के समान थ्रेड में अनलॉक करना होगा। मैंने pthread_mutex_destroy() करते समय इन त्रुटियों को देखा क्योंकि मेरे पास एक निर्माता, एकल उपभोक्ता स्थिति थी इसलिए डेडलॉक नहीं हुआ था।

pthread_cond_wait() लौटने पर म्यूटेक्स को लॉक करना माना जाता है, और यह हो सकता था, लेकिन अंतिम अनलॉक नहीं हुआ क्योंकि हमने जोर से धागे को रद्द कर दिया था। सुरक्षा के लिए मैं आम तौर पर pthread_cancel() का उपयोग करने से बचने की कोशिश करता हूं क्योंकि कुछ प्लेटफार्म इसका समर्थन भी नहीं करते हैं। आप एक अस्थिर बूल या परमाणु का उपयोग कर सकते हैं और जांच सकते हैं कि थ्रेड बंद होना चाहिए या नहीं। इस तरह, म्यूटेक्स को भी साफ-सुथरा किया जाएगा।