C ++クリティカルセクションが機能しない

c++ critical-section vector
C ++クリティカルセクションが機能しない

クリティカルセクションのコードが機能しません!!! Backgrounder.runはMESSAGE_QUEUE g_msgQueueを変更でき、LockSectionsデストラクタはまだ呼び出されていませんでした!!!

追加コード:

typedef std::vector 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
#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

  0  0


ベストアンサー

1つに2つの質問。 最初のものについては知りませんが、クリティカルセクションの部分は簡単に説明できます。 バックグラウンドスレッドはロックを要求しないため、もちろんブロックされません。 スレッドがロックできるように、クリティカルセクションオブジェクトを `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」でなければならないことに注意してください。

3


このコードには多くの問題があります。

まず第一に、標準のコンテナから派生することはほとんど常に悪い考えです。 この場合、プライベート継承を使用しているため、問題は軽減されますが、完全に排除されるわけではありません。 いずれにせよ、とにかく多くの(何か?)用途に継承を置くようには見えません。 MESSAGE_LIST(実際は` std

vector`)から MESSAGE_QUEUE`を派生しましたが、とにかく MESSAGE_LIST`のインスタンスへのポインタを `MESSAGE_QUEUE`に埋め込みます。

第二に、スレッド間で通信するためにキューを使用する場合(一般的には良い考えだと思います)、各スレッドがロックを正しく管理することを要求するのではなく、キュー操作に固有のロックを行う必要があります。

3番目に、「ベクター」は、キューを表すのに特に適したデータ構造ではありません。ただし、サイズを固定して、ほぼリングバッファーのように使用する場合を除きます。 それも悪い考えではありませんが、あなたがやったこととはかなり異なります。 サイズを動的にする場合は、代わりにdequeから始める方がよいでしょう。

4番目に、エラー処理のマジック番号(0x1AE1、0x1AE2など)は非常に不透明です。 少なくとも、これらの意味のある名前を付ける必要があります。 あなたが持っている1つのコメントは、明確に近い場所で使用しません。

最後に、スレッドセーフキューのコードを書くというすべての問題に取り組む場合は、特定のタイプに専念するのではなく、基本的に任意の種類のデータを保持できるように汎用化することもできます。

最終的に、あなたのコードは、Windowsの機能を直接使用することで、クライアントに多くの作業やトラブルを保存しないようです。 ほとんどの場合、わずかに異なる名前で同じ機能を提供しました。

IMO、スレッドセーフキューはほとんどすべての作業を内部で処理する必要があるため、クライアントコードは他のキューと同様に使用できます。

// Warning: untested code.
// Assumes: `T::T(T const &) throw()`
//
template
class queue {
    std::deque 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 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;
}

3


バックグラウンドスレッドは同じ CriticalSection`オブジェクトにアクセスする必要があり、ロックするために LockSection`オブジェクトを作成する必要があります。ロックは共同作業です。

2


あなたはそれをポップした後の最後の要素を返そうとしています。

1


タイトルとURLをコピーしました