2011-08-19 56 views
5

Chương trình của tôi bị lỗi ngẫu nhiên trong một kịch bản nhỏ mà tôi có thể sao chép, nhưng nó xảy ra trong mlock.c (đó là tệp thời gian chạy VC++) từ ntdll.dll và tôi không thể thấy ngăn xếp dấu vết. Tôi biết rằng nó xảy ra trong một trong các chức năng thread của tôi, mặc dù.VC++ 2010: Lỗi phần nghiêm trọng kỳ lạ

Đây là mã mlock.c nơi chương trình bị treo:

void __cdecl _unlock (
     int locknum 
     ) 
{ 
     /* 
     * leave the critical section. 
     */ 
     LeaveCriticalSection(_locktable[locknum].lock); 
} 

Lỗi này là "xử lý không hợp lệ theo quy định". Nếu tôi nhìn vào locknum, đó là một số lớn hơn kích thước của _locktable, do đó, điều này làm cho một số ý nghĩa.

Điều này dường như liên quan đến việc sử dụng Mục quan trọng. Tôi sử dụng CRITICAL_SECTIONS trong chủ đề của mình, thông qua lớp trình bao bọc CCriticalSection và trình bảo vệ RAII liên quan của nó, CGuard. Định nghĩa cho cả hai here để tránh lộn xộn hơn nữa.

Đây là chức năng thread đó là đâm:

unsigned int __stdcall CPlayBack::timerThread(void * pParams) { 
#ifdef _DEBUG 
    DRA::CommonCpp::SetThreadName(-1, "CPlayBack::timerThread"); 
#endif 
    CPlayBack * pThis = static_cast<CPlayBack*>(pParams); 
    bool bContinue = true; 
    while(bContinue) { 
     float m_fActualFrameRate = pThis->m_fFrameRate * pThis->m_fFrameRateMultiplier; 
     if(m_fActualFrameRate != 0 && pThis->m_bIsPlaying) { 
      bContinue = (::WaitForSingleObject(pThis->m_hEndThreadEvent, static_cast<DWORD>(1000.0f/m_fActualFrameRate)) == WAIT_TIMEOUT); 
      CImage img; 
      if(pThis->m_bIsPlaying && pThis->nextFrame(img)) 
       pThis->sendImage(img); 
     } 
     else 
      bContinue = (::WaitForSingleObject(pThis->m_hEndThreadEvent, 10) == WAIT_TIMEOUT); 
    } 
    ::GetErrorLoggerInstance()->Log(LOG_TYPE_NOTE, "CPlayBack", "timerThread", "Exiting thread"); 
    return 0; 
} 

đâu CCriticalSection vào được không? Mọi đối tượng CImage chứa đối tượng CCriticalSection mà nó sử dụng thông qua khóa RAII CGuard. Hơn nữa, mỗi CImage chứa đối tượng CSharedMemory thực hiện đếm tham chiếu. Cuối cùng, nó chứa hai số CCriticalSection, một cho dữ liệu và một cho bộ đếm tham chiếu. Một ví dụ điển hình của những tương tác được tốt nhất nhìn thấy trong destructors:

CImage::~CImage() { 
    CGuard guard(m_csData); 
    if(m_pSharedMemory != NULL) { 
     m_pSharedMemory->decrementUse(); 
     if(!m_pSharedMemory->isBeingUsed()){ 
      delete m_pSharedMemory; 
      m_pSharedMemory = NULL; 
     } 
    } 
    m_cProperties.ClearMin(); 
    m_cProperties.ClearMax(); 
    m_cProperties.ClearMode(); 
} 

CSharedMemory::~CSharedMemory() { 
    CGuard guardUse(m_cs); 
    if(m_pData && m_bCanDelete){ 
     delete []m_pData; 
    } 
    m_use = 0; 
    m_pData = NULL; 
} 

Bất cứ ai đụng vào loại hình báo lỗi? Bất kì lời đề nghị nào?

Chỉnh sửa: Tôi đã thấy một số ngăn xếp cuộc gọi: cuộc gọi đến từ ~ CSharedMemory. Vì vậy, phải có một số điều kiện chủng tộc có

Sửa: Mã More CSharedMemory here

+0

Tham nhũng bộ nhớ? –

+0

Cả hai lớp đều trông ổn. Bạn có thể hiển thị một số mã liên quan đến CÁCH bạn đang sử dụng chúng không? Bạn có chắc chắn các nhà xây dựng được gọi đúng cách trước khi sử dụng (không có tranh chấp thread trên các nhà xây dựng?). Chúng có được phân bổ động (vì một lý do nào đó) không? – Chad

+0

Lớp học của bạn không có liên quan gì đến mã CRT, nó sử dụng Windows. Gỡ lỗi cuộc đua luồng và tham nhũng đống không bao giờ là vui vẻ, chúc may mắn với nó. –

Trả lời

1

Tôi quyết định tuân thủ nguyên tắc KISS và đá và cuộn tất cả các trang đơn giản hóa mọi thứ. Tôi đã hình dung rằng mình sẽ thay thế CSharedMemoryClass bằng một số std::tr1::shared_ptr<BYTE>CCriticalSection để bảo vệ nó khỏi quyền truy cập đồng thời. Cả hai đều là thành viên của CImage hiện tại và các mối quan tâm được tách biệt tốt hơn ngay bây giờ, IMHO.

Điều đó giải quyết phần quan trọng kỳ lạ, nhưng bây giờ có vẻ như tôi bị rò rỉ bộ nhớ gây ra bởi std::tr1::shared_ptr, bạn có thể thấy tôi đăng bài về nó sớm ... Nó không bao giờ kết thúc!

5

Các "không hợp lệ xử lý theo quy định" mã trở lại vẽ nên một bức tranh khá rõ ràng rằng đối tượng phần quan trọng của bạn đã được deallocated; giả định tất nhiên là nó đã được cấp phát đúng cách để bắt đầu.

Lớp RAII của bạn có vẻ như là thủ phạm. Nếu bạn lùi lại một bước và nghĩ về nó, lớp RAII của bạn vi phạm nguyên tắc Sepration Of Concerns, bởi vì nó có hai nhiệm vụ:

  1. Nó cung cấp phân bổ/hủy ngữ nghĩa cho các CRITICAL_SECTION
  2. Nó cung cấp ngữ nghĩa Acquire/phát hành cho the CRITICAL_SECTION

Hầu hết các trình cài đặt của trình bao bọc CS Tôi đã thấy vi phạm nguyên tắc SoC theo cùng một cách nhưng có thể có vấn đề. Đặc biệt là khi bạn phải bắt đầu đi qua các phiên bản của lớp để có được chức năng nhận/giải phóng. Hãy xem xét một đơn giản, ví dụ contrived trong psudocode:

void WorkerThreadProc(CCriticalSection cs) 
{ 
    cs.Enter(); 
    // MAGIC HAPPENS 
    cs.Leave(); 
} 

int main() 
{ 
    CCriticalSection my_cs; 
    std::vector<NeatStuff> stuff_used_by_multiple_threads; 

    // Create 3 threads, passing the entry point "WorkerThreadProc" 
    for(int i = 0; i < 3; ++i) 
    CreateThread(... &WorkerThreadProc, my_cs); 

    // Join the 3 threads... 
    wait(); 
} 

Vấn đề ở đây là CCriticalSection được truyền theo giá trị, do đó destructor được gọi là 4 lần. Mỗi lần destructor được gọi, CRITICAL_SECTION được deallocated. Lần đầu tiên hoạt động tốt, nhưng bây giờ nó đã biến mất.

Bạn có thể kludge xung quanh vấn đề này bằng cách chuyển tài liệu tham khảo hoặc con trỏ đến lớp phần quan trọng, nhưng sau đó bạn bùn các vùng biển ngữ nghĩa với các vấn đề quyền sở hữu. Điều gì sẽ xảy ra nếu luồng "sở hữu" bit crit chết trước các luồng khác? Bạn có thể sử dụng một shared_ptr, nhưng bây giờ không ai thực sự "sở hữu" phần quan trọng, và bạn đã từ bỏ một chút kiểm soát trong khu vực để đạt được một chút trong khu vực khác.

"Sửa lỗi" thực sự cho vấn đề này là phân biệt các mối quan tâm. Có một lớp phân bổ & deallocation:

class CCriticalSection : public CRITICAL_SECTION 
{ 
public: 
    CCriticalSection(){ InitializeCriticalSection(this); } 
    ~CCriticalSection() { DestroyCriticalSection(this); } 
}; 

... và khác để xử lý khóa & mở khóa ...

class CSLock 
{ 
public: 
    CSLock(CRITICAL_SECTION& cs) : cs_(cs) { EnterCriticalSection(&cs_); } 
    ~CSLock() { LeaveCriticalSection(&cs_); } 
private: 
    CRITICAL_SECTION& cs_; 
}; 

Bây giờ bạn có thể vượt qua xung quanh con trỏ thô hoặc tham chiếu đến một đối tượng CCriticalSection duy nhất, có thể const, và có các luồng công nhân khởi tạo CSLocks của riêng nó trên nó. CSLock được sở hữu bởi chủ đề tạo ra nó, điều này là đúng, nhưng quyền sở hữu của CCriticalSection rõ ràng được giữ lại bởi một số luồng điều khiển; cũng là một điều tốt.

+0

Tôi không thấy điều này khác với những gì tôi đã làm trước đây, ngoại trừ nó sử dụng thừa kế thay vì thành phần và gọi hàm EnterCriticalSection và LeaveCriticalSection tương đương với lớp CGuard của tôi . Tôi làm được gì khi làm theo cách đó? –

+0

Bạn đã thay đổi câu hỏi của mình thành điểm mà bây giờ chúng ta không biết CCriticalSection trông như thế nào, CGuard là gì, hoặc cách liên quan. Bạn nói những thứ như "Mỗi đối tượng CImage chứa đối tượng CCriticalSection mà nó sử dụng thông qua khóa CGUard RAII" nhưng trừ khi chúng ta thấy cách chúng được khai báo và triển khai thực hiện, chúng ta không thực sự biết điều đó có nghĩa là gì. –

+0

Xin lỗi, tôi đã xóa mã đó vì ai đó đã nói với tôi rằng các lớp của tôi không sao và tránh làm lộn xộn câu hỏi. Tôi đã đăng nó trong Ideone.com, xem liên kết trong câu hỏi được cập nhật của tôi –

1
  • Đảm bảo đối tượng Mục quan trọng không nằm trong số #pragma đóng gói 1 (hoặc bất kỳ bao bì không mặc định).
  • Đảm bảo rằng không có chuỗi nào khác (hoặc cùng một chuỗi) đang làm hỏng đối tượng CS. Chạy một số công cụ phân tích tĩnh để kiểm tra bất kỳ vấn đề tràn bộ đệm nào.
  • Nếu bạn có công cụ phân tích thời gian chạy, hãy chạy nó để tìm sự cố.
Các vấn đề liên quan