簡體   English   中英

C ++關鍵部分不起作用

[英]C++ Critical Section not working

我的關鍵部分代碼不起作用!!! Backgrounder.run能夠修改MESSAGE_QUEUE g_msgQueue,並且尚未調用LockSections析構函數!

額外的代碼:

typedef std::vector<int> MESSAGE_LIST; // SHARED OBJECT .. MUST LOCK!

class MESSAGE_QUEUE : MESSAGE_LIST{
public:
    MESSAGE_LIST * m_pList;
    MESSAGE_QUEUE(MESSAGE_LIST* pList){ m_pList = pList; }
    ~MESSAGE_QUEUE(){ }
    /* This class will be shared between threads that means any 
     * attempt to access it MUST be inside a critical section. 
     */
    void Add( int messageCode ){ if(m_pList) m_pList->push_back(messageCode); }

    int getLast()
    { 
      if(m_pList){ 
        if(m_pList->size() == 1){ 
          Add(0x0); 
        } 
        m_pList->pop_back(); 
        return m_pList->back(); 
      } 
    }
    void removeLast()
    { 
      if(m_pList){ 
        m_pList->erase(m_pList->end()-1,m_pList->end()); 
      } 
    }
};

class Backgrounder{
public:
    MESSAGE_QUEUE* m_pMsgQueue;
    static void __cdecl Run( void* args){
        MESSAGE_QUEUE* s_pMsgQueue = (MESSAGE_QUEUE*)args;
        if(s_pMsgQueue->getLast() == 0x45)printf("It's a success!");
        else printf("It's a trap!");
    }
    Backgrounder(MESSAGE_QUEUE* pMsgQueue) 
    { 
      m_pMsgQueue = pMsgQueue; 
      _beginthread(Run,0,(void*)m_pMsgQueue); 
    }
    ~Backgrounder(){ }
};

int main(){

    MESSAGE_LIST g_List;
    CriticalSection crt;
    ErrorHandler err;
    LockSection lc(&crt,&err); // Does not work , see question #2
    MESSAGE_QUEUE g_msgQueue(&g_List);
    g_msgQueue.Add(0x45); 
    printf("%d",g_msgQueue.getLast()); 
    Backgrounder back_thread(&g_msgQueue);


    while(!kbhit());
    return 0;
}

#ifndef CRITICALSECTION_H
#define CRITICALSECTION_H
#include <windows.h>
#include "ErrorHandler.h"


class CriticalSection{
    long m_nLockCount;
    long m_nThreadId;
    typedef CRITICAL_SECTION cs;
    cs m_tCS;
public:
    CriticalSection(){
        ::InitializeCriticalSection(&m_tCS);
        m_nLockCount = 0;
        m_nThreadId = 0;
    }
    ~CriticalSection(){ ::DeleteCriticalSection(&m_tCS); }
    void Enter(){ ::EnterCriticalSection(&m_tCS);  }
    void Leave(){  ::LeaveCriticalSection(&m_tCS); }
    void Try();
};


class LockSection{
    CriticalSection* m_pCS;
    ErrorHandler * m_pErrorHandler;
    bool m_bIsClosed;
public:
    LockSection(CriticalSection* pCS,ErrorHandler* pErrorHandler){
        m_bIsClosed = false;
        m_pCS = pCS;
        m_pErrorHandler = pErrorHandler;
            // 0x1AE is code prefix for critical section header
        if(!m_pCS)m_pErrorHandler->Add(0x1AE1); 
        if(m_pCS)m_pCS->Enter();
    }
    ~LockSection(){
        if(!m_pCS)m_pErrorHandler->Add(0x1AE2);
        if(m_pCS && m_bIsClosed == false)m_pCS->Leave();
    }
    void ForceCSectionClose(){
        if(!m_pCS)m_pErrorHandler->Add(0x1AE3);
        if(m_pCS){m_pCS->Leave();m_bIsClosed = true;}
    }
};

/*

Safe class basic structure;

class SafeObj
{
     CriticalSection m_cs;

public:
    void SafeMethod()
    {
        LockSection myLock(&m_cs);
        //add code to implement the method ...

    }
};



*/
#endif

兩個問題合而為一。 我不了解第一部分,但是關鍵部分很容易解釋。 后台線程並沒有試圖要求鎖定,因此當然不會被阻塞。 您需要使關鍵部分對象crt對線程可見,以便它可以鎖定它。

使用此鎖類的方法是,要序列化的代碼的每個部分都必須創建一個LockSection對象,並將其保留到序列化塊的末尾:

線程1:

{
    LockSection lc(&crt,&err);
    //operate on shared object from thread 1
}

線程2:

{
    LockSection lc(&crt,&err);
    //operate on shared object from thread 2
}

注意,它必須與要序列化的每個代碼塊中使用的臨界區實例crt相同。

這段代碼有很多問題。

首先,從標准容器派生幾乎總是一個壞主意。 在這種情況下,您將使用私有繼承,這樣可以減少問題,但不能完全消除問題。 無論如何,您似乎都不會對繼承進行過多的使用。 即使你已經派生的MESSAGE_QUEUEMESSAGE_LIST (這實際上是std::vector<int> ),你嵌入一個指向一個實例MESSAGE_LISTMESSAGE_QUEUE反正。

第二,如果你打算使用一個隊列中的線程間通信(我認為這是通常一個好主意),你應該在隊列操作鎖定固有的而不是要求每個線程正確地管理自身的鎖定。

第三, vector無論如何都不是用於表示隊列的特別合適的數據結構,除非您將其設置為固定大小,並大致像環形緩沖區那樣使用它。 這也不是一個壞主意,但是與您所做的有很大的不同。 如果要使大小動態變化,最好從雙端隊列開始。

第四,錯誤處理中的幻數(0x1AE1、0x1AE2等)非常不透明。 至少,您需要給這些有意義的名稱。 您所發表的一條評論並未在任何地方都可以使用。

最后,如果您要為編寫線程安全隊列的代碼而煩惱,最好將其設為通用,這樣它就可以保存所需的任何類型的數據,而不是將其專用於一種特定類型。

最終,您的代碼似乎並沒有為客戶端節省大量工作或直接使用Windows函數帶來的麻煩。 在大多數情況下,您只是以略有不同的名稱提供了相同的功能。

IMO,線程安全隊列應該在內部處理幾乎所有工作,以便客戶端代碼可以像使用其他隊列一樣使用它。

// Warning: untested code.
// Assumes: `T::T(T const &) throw()` 
//
template <class T>
class queue { 
    std::deque<T> data;
    CRITICAL_SECTION cs;
    HANDLE semaphore;
public:
    queue() { 
        InitializeCriticalSection(&cs); 
        semaphore = CreateSemaphore(NULL, 0, 2048, NULL);
    }

    ~queue() { 
        DeleteCriticalSection(&cs); 
        CloseHandle(semaphore);
    }

    void push(T const &item) {         
        EnterCriticalSection(&cs);
        data.push_back(item);
        LeaveCriticalSection(&cs);
        ReleaseSemaphore(semaphore, 1, NULL);
    }

    T pop() { 
        WaitForSingleObject(semaphore, INFINITE);
        EnterCriticalSection(&cs);
        T item = data.front();
        data.pop_front();
        LeaveCriticalSection(&cs);
        return item;
    }
};



HANDLE done;

typedef queue<int> msgQ;

enum commands { quit, print };

void backgrounder(void *qq) { 

  // I haven't quite puzzled out what your background thread
  // was supposed to do, so I've kept it really simple, executing only
  // the two commands listed above.
  msgQ *q = (msgQ *)qq;
  int command;

  while (quit != (command = q->pop()))
      printf("Print\n");
  SetEvent(done);
}

int main() { 
    msgQ q;
    done = CreateEvent(NULL, false, false, NULL);
    _beginthread(backgrounder, 0, (void*)&q);
    for (int i=0; i<20; i++)
        q.push(print);
    q.push(quit);
    WaitForSingleObject(done, INFINITE);
    return 0;
}

您的后台線程需要訪問相同的CriticalSection對象,並且需要創建LockSection對象來鎖定它-鎖定是協作的。

您嘗試彈出最后一個元素返回它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM