2011-07-28 21 views
7

Là một phần của dự án tôi đang viết chức năng ghi nhật ký. Hàm logger này gửi một e-mail khi chương trình muốn đăng nhập một cái gì đó. Vì nó đã xảy ra rằng máy chủ SMTP không đáp ứng, tôi đã quyết định thực hiện việc gửi các thư trong một chuỗi riêng biệt. Chủ đề này đọc tin nhắn từ một std :: deque được điền bởi chức năng ghi nhật ký. Các chủ đề được thiết lập như sau:Xác nhận trên mutex khi sử dụng nhiều chủ đề và mutexes

while (!boost::this_thread::interruption_requested()) 
{ 
    EmailItem emailItem; 
    { 
    boost::unique_lock<boost::mutex> lock(mMutex); 
    while (mEmailBuffer.empty()) 
     mCond.wait(lock); 

    bufferOverflow = mBufferOverflow; 
    mBufferOverflow = false; 
    nrOfItems = mEmailBuffer.size(); 

    if (nrOfItems > 0) 
    { 
     emailItem = mEmailBuffer.front(); 
     mEmailBuffer.pop_front(); 
    } 
    } 

    if (nrOfItems > 0) 
    { 
     bool sent = false; 
     while(!sent) 
     { 
      try 
      { 
      ..... Do something with the message ..... 
      { 
       boost::this_thread::disable_interruption di; 
       boost::lock_guard<boost::mutex> lock(mLoggerMutex); 
       mLogFile << emailItem.mMessage << std::endl; 
      } 
      sent = true; 
      } 
      catch (const std::exception &e) 
      { 
      // Unable to send mail, an exception occurred. Retry sending it after some time 
      sent = false; 
      boost::this_thread::sleep(boost::posix_time::seconds(LOG_WAITBEFORE_RETRY)); 
      } 
     } 
    } 
} 

Chức năng log() thêm tin nhắn mới đến deque (mEmailBuffer) như sau:

{ 
    boost::lock_guard<boost::mutex> lock(mMutex); 
    mEmailBuffer.push_back(e); 
    mCond.notify_one(); 
} 

Khi thoát khỏi chương trình chính, destructor của đối tượng logger được gọi. Đây là nơi mà nó đi sai, ứng dụng bị treo với một lỗi:

/usr/include/boost/thread/pthread/mutex.hpp:45: boost::mutex::~mutex(): Assertion `!pthread_mutex_destroy(&m)' failed. 

Destructor chỉ gọi một ngắt trên thread và sau đó gia nhập nó:

mQueueThread.interrupt(); 
mQueueThread.join(); 

Trong chương trình chính, tôi sử dụng nhiều các lớp khác nhau sử dụng luồng tăng cường và mutex, điều này có thể gây ra hành vi này không? Không gọi destructor của đối tượng logger không có lỗi, cũng như sử dụng đối tượng logger và không làm bất cứ điều gì khác.

Tôi đoán là tôi đang làm điều gì đó rất sai, hoặc có một lỗi trong thư viện luồng khi sử dụng nhiều luồng được chia thành nhiều lớp. Có ai có ý tưởng về lý do của lỗi này không?

EDIT: Tôi đã làm @Andy T đề xuất và tước mã càng nhiều càng tốt. Tôi đã xóa hầu hết mọi thứ trong hàm đang chạy trong một chuỗi khác. Chủ đề bây giờ trông giống như:

void Vi::Logger::ThreadedQueue() 
{ 
    bool bufferOverflow = false; 
    time_t last_overflow = 0; 
    unsigned int nrOfItems = 0; 

    while (!boost::this_thread::interruption_requested()) 
    { 
    EmailItem emailItem; 
    // Check for new log entries 
    { 
     boost::unique_lock<boost::mutex> lock(mMutex); 
     while (mEmailBuffer.empty()) 
     mCond.wait(lock); 
    } 
    } 
} 

Sự cố vẫn tiếp diễn. Backtracking của vấn đề tuy nhiên chỉ cho tôi một cái gì đó khác với mã ban đầu:

#0 0x00007ffff53e9ba5 in raise (sig=<value optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 
#1 0x00007ffff53ed6b0 in abort() at abort.c:92 
#2 0x00007ffff53e2a71 in __assert_fail (assertion=0x7ffff7bb6407 "!pthread_mutex_lock(&m)", file=<value optimized out>, line=50, function=0x7ffff7bb7130 "void boost::mutex::lock()") at assert.c:81 
#3 0x00007ffff7b930f3 in boost::mutex::lock (this=0x7fffe2c1b0b8) at /usr/include/boost/thread/pthread/mutex.hpp:50 
#4 0x00007ffff7b9596c in boost::unique_lock<boost::mutex>::lock (this=0x7fffe48b3b40) at /usr/include/boost/thread/locks.hpp:349 
#5 0x00007ffff7b958db in boost::unique_lock<boost::mutex>::unique_lock (this=0x7fffe48b3b40, m_=...) at /usr/include/boost/thread/locks.hpp:227 
#6 0x00007ffff6ac2bb7 in Vi::Logger::ThreadedQueue (this=0x7fffe2c1ade0) at /data/repos_ViNotion/stdcomp/Logging/trunk/src/Logger.cpp:198 
#7 0x00007ffff6acf2b2 in boost::_mfi::mf0<void, Vi::Logger>::operator() (this=0x7fffe2c1d890, p=0x7fffe2c1ade0) at /usr/include/boost/bind/mem_fn_template.hpp:49 
#8 0x00007ffff6acf222 in boost::_bi::list1<boost::_bi::value<Vi::Logger*> >::operator()<boost::_mfi::mf0<void, Vi::Logger>, boost::_bi::list0> (this=0x7fffe2c1d8a0, f=..., a=...) at /usr/include/boost/bind/bind.hpp:253 
#9 0x00007ffff6acf1bd in boost::_bi::bind_t<void, boost::_mfi::mf0<void, Vi::Logger>, boost::_bi::list1<boost::_bi::value<Vi::Logger*> > >::operator() (this=0x7fffe2c1d890) at /usr/include/boost/bind/bind_template.hpp:20 
#10 0x00007ffff6aceff2 in boost::detail::thread_data<boost::_bi::bind_t<void, boost::_mfi::mf0<void, Vi::Logger>, boost::_bi::list1<boost::_bi::value<Vi::Logger*> > > >::run (this=0x7fffe2c1d760) 
    at /usr/include/boost/thread/detail/thread.hpp:56 
#11 0x00007ffff2cc5230 in thread_proxy() from /usr/lib/libboost_thread.so.1.42.0 
#12 0x00007ffff4d87971 in start_thread (arg=<value optimized out>) at pthread_create.c:304 
#13 0x00007ffff549c92d in clone() at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112 
#14 0x0000000000000000 in ??() 

nó có thể là có thể là mMutex không mở khóa trong sự kết hợp của việc sử dụng một unique_lock() và sau đó làm gián đoạn các chủ đề?

Trả lời

3

bạn có tham gia chủ đề của mình trước khi thoát không? như tyz được đề xuất, chuỗi của bạn vẫn có thể giữ cho nó bị khóa khi mutex bị hủy.

[EDIT]

bạn không cung cấp ví dụ hoàn chỉnh có thể được biên dịch và chạy, thật khó để giúp bạn.

kiểm tra ví dụ này đơn giản mà phải tương tự như một của bạn:

#include <boost/thread.hpp> 
#include <boost/bind.hpp> 
#include <queue> 

class Test 
{ 
public: 
    Test() 
    { 
     thread = boost::thread(boost::bind(&Test::thread_func, this)); 
    } 

    ~Test() 
    { 
     thread.interrupt(); 
     thread.join(); 
    } 

    void run() 
    { 
     for (size_t i = 0; i != 10000; ++i) { 
      boost::lock_guard<boost::mutex> lock(mutex); 
      queue.push(i); 
      condition_var.notify_one(); 
     } 
    } 

private: 
    void thread_func() 
    { 
     while (!boost::this_thread::interruption_requested()) 
     { 
      { 
       boost::unique_lock<boost::mutex> lock(mutex); 
       while (queue.empty()) 
        condition_var.wait(lock); 
       queue.pop(); 
      } 
     } 
    } 

private: 
    boost::thread thread; 
    boost::mutex mutex; 
    boost::condition_variable condition_var; 
    std::queue<int> queue; 
}; 

int main() 
{ 
    Test test; 
    test.run(); 

    return 0; 
} 

so sánh với trường hợp của bạn

+0

Có, tôi tham gia chuỗi trước khi thoát. Ngoài ra, theo như tôi biết, không có điểm ngắt ở những nơi mà một mutex bị khóa trong chuỗi. Do đó, tôi sẽ không mong đợi các thread vẫn còn có một mutex bị khóa khi nó bị gián đoạn. – Tim

+0

xin vui lòng kiểm tra lại một lần nữa rằng mutex của bạn (đúng) không bị xóa trước khi tham gia –

+0

Cả hai mutexes là biến thành viên của lớp Logger. Hàm hủy của lớp này gọi hàm 'interrupt()' và 'join()' trên luồng. Do đó, các mutex sẽ vẫn còn tồn tại khi chuỗi được nối, nếu tôi đúng. – Tim

1

Bạn nên mở khóa mutex trước khi xóa.

+0

Cảm ơn trả lời của bạn. Trong mã của tôi, tôi chỉ sử dụng ổ khóa có khóa để khóa các mutex. Vì vậy, họ nên được mở khóa tự động khi phạm vi được đóng lại, phải không? – Tim

+0

@Tim, tôi đã cố gắng hết sức nhưng tôi không tìm thấy bất kỳ lỗi nào bên trong mã đã cho.Tuy nhiên, lỗi bạn nhận được thường là do xóa bỏ mutex bị khóa. –

+0

Tôi đã thêm mở khóa các mutex một cách rõ ràng ở cuối hàm, rất tiếc là không thành công: mMutex.unlock(); mLoggerMutex.unlock(); – Tim

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