2011-02-16 52 views
6

Tôi đang gặp vấn đề với pthreads nơi tôi nghĩ rằng tôi đang nhận được một bế tắc. Tôi đã tạo ra một hàng đợi chặn mà tôi nghĩ đã làm việc, nhưng sau khi làm một số thử nghiệm nhiều hơn tôi đã thấy rằng nếu tôi cố gắng và hủy bỏ nhiều chủ đề đang chặn trên blocking_queue, tôi dường như để có được một bế tắc.C + + ngăn chặn hàng đợi chặn bế tắc (tôi nghĩ)

Hàng đợi chặn là rất đơn giản và trông như thế này:

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

Để thử nghiệm, tôi đã tạo ra 4 đề mà kéo vào hàng đợi chặn này. Tôi đã thêm một số câu lệnh in vào hàng đợi chặn và mỗi luồng đang chuyển sang phương thức pthread_cond_wait(). Tuy nhiên, khi tôi cố gắng gọi pthread_cancel() và pthread_join() trên mỗi luồng chương trình chỉ bị treo.

Tôi cũng đã thử nghiệm điều này chỉ với một chuỗi và hoạt động hoàn hảo.

Theo tài liệu, pthread_cond_wait() là điểm hủy, do đó, việc hủy cuộc gọi trên các chuỗi đó sẽ khiến chúng ngừng thực thi (và thao tác này chỉ hoạt động với 1 chuỗi). Tuy nhiên pthread_mutex_lock không phải là một điểm hủy bỏ. Có thể một cái gì đó đang xảy ra dọc theo dòng khi pthread_cancel() được gọi, chủ đề bị hủy bỏ thu hút mutex trước khi chấm dứt và không mở khóa nó, và sau đó khi thread tiếp theo bị hủy, nó không thể thu được mutex và deadlocks? Hay có điều gì khác mà tôi đang làm sai.

Mọi lời khuyên đều đáng yêu. Cảm ơn :)

+0

hãy thử sử dụng [Helgrind] (http://valgrind.org/info/tools.html#helgrind), nó rất hữu ích trong quá khứ đối với tôi để phát hiện tình trạng đua và deadlocks. – Flexo

+0

Việc hủy có thể gây nguy hiểm. Vui lòng cho chúng tôi biết thêm về logic của bạn: trạng thái hủy của chủ đề công nhân của bạn là gì?Xử lý dọn dẹp nào? Và chính xác như thế nào là bạn sắp xếp các cuộc gọi để hủy bỏ/tham gia vào nhiều chủ đề? – pilcrow

Trả lời

5

pthread_cancel() là tốt nhất nên tránh.

Bạn có thể bỏ chặn tất cả các chuỗi của mình bị chặn trên Blocking_Queue :: pull() bằng cách ném một ngoại lệ từ đó.

Một điểm yếu trong hàng đợi là T t = _queue.front(); gọi hàm tạo bản sao của T có thể ném ngoại lệ, hiển thị cho bạn hàng đợi bị khóa vĩnh viễn. Sử dụng tốt hơn ổ khóa có khóa C++.

Dưới đây là một ví dụ về duyên dáng chấm dứt chủ đề:

$ 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

Đây là một giải pháp rất tốt cho vấn đề của tôi, cảm ơn vì đã chia sẻ nó! :) – vimalloc

1

Tôi không quen thuộc với pthread_cancel() - Tôi thích chấm dứt hợp tác.

Sẽ không phải là một pthread_cancel() để lại mutex của bạn bị khóa? Tôi cho rằng bạn cần dọn dẹp với trình xử lý hủy.

+0

Trong lý thuyết khi pthread_cond_wait() được gọi, mutex sẽ được giải phóng (và nó là, bởi vì nhiều luồng làm cho nó thành câu lệnh pthread_cond_wait()). Tuy nhiên khi pthread_cancel được gọi là nó trông giống như mutex đang được yêu cầu, ít nhất đó là lời giải thích của tôi cho nó. – vimalloc

+1

Các mutex willl thực sự được reacquired (nếu pthread_cond_wait() bị hủy bỏ). Xem http://pubs.opengroup.org/onlinepubs/007908799/xsh/pthread_cond_wait.html "Chờ đợi điều kiện (cho dù có định thời gian hay không) là điểm hủy bỏ. Khi trạng thái cho phép hủy của một luồng được đặt thành PTHREAD_CANCEL_DEFERRED, một tác dụng phụ của hành động theo yêu cầu hủy trong khi trong điều kiện chờ đợi là mutex là (có hiệu lực) được mua lại trước khi gọi trình xử lý dọn dẹp hủy đầu tiên. " – sstn

1

Tôi đã có trải nghiệm tương tự với pthread_cond_wait()/pthread_cancel(). Tôi có vấn đề với một khóa vẫn đang được tổ chức sau khi thread trả về vì lý do nào đó, và nó đã không thể mở khóa nó, vì bạn phải mở khóa trong cùng một chủ đề như bạn bị khóa. Tôi nhận thấy những lỗi này khi thực hiện pthread_mutex_destroy() vì tôi có một nhà sản xuất duy nhất, một tình huống tiêu dùng duy nhất để bế tắc không xảy ra.

pthread_cond_wait() là nghĩa vụ phải khóa mutex khi quay trở lại, và điều này có thể xảy ra, nhưng mở khóa cuối cùng đã không đi qua kể từ khi chúng tôi buộc hủy bỏ các chủ đề. Vì sự an toàn, tôi thường cố gắng tránh sử dụng pthread_cancel() hoàn toàn vì một số nền tảng thậm chí không hỗ trợ điều này. Bạn có thể sử dụng một bool dễ bay hơi hoặc nguyên tử và kiểm tra xem thread nên được tắt. Bằng cách đó, các mutex sẽ được xử lý một cách rõ ràng.

Các vấn đề liên quan