简体   繁体   English

C ++关键部分不起作用

[英]C++ Critical Section not working

My critical section code does not work!!! 我的关键部分代码不起作用!!! Backgrounder.run IS able to modify MESSAGE_QUEUE g_msgQueue and LockSections destructor hadn't been called yet !!! Backgrounder.run能够修改MESSAGE_QUEUE g_msgQueue,并且尚未调用LockSections析构函数!

Extra code : 额外的代码:

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

Two questions in one. 两个问题合而为一。 I don't know about the first, but the critical section part is easy to explain. 我不了解第一部分,但是关键部分很容易解释。 The background thread isn't trying to claim the lock and so, of course, is not blocked. 后台线程并没有试图要求锁定,因此当然不会被阻塞。 You need to make the critical section object crt visible to the thread so that it can lock it. 您需要使关键部分对象crt对线程可见,以便它可以锁定它。

The way to use this lock class is that each section of code that you want serialised must create a LockSection object and hold on to it until the end of the serialised block: 使用此锁类的方法是,要序列化的代码的每个部分都必须创建一个LockSection对象,并将其保留到序列化块的末尾:

Thread 1: 线程1:

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

Thread 2: 线程2:

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

Note that it has to be the same critical section instance crt that is used in each block of code that is to be serialised. 注意,它必须与要序列化的每个代码块中使用的临界区实例crt相同。

This code has a number of problems. 这段代码有很多问题。

First of all, deriving from the standard containers is almost always a poor idea. 首先,从标准容器派生几乎总是一个坏主意。 In this case you're using private inheritance, which reduces the problems, but doesn't eliminate them entirely. 在这种情况下,您将使用私有继承,这样可以减少问题,但不能完全消除问题。 In any case, you don't seem to put the inheritance to much (any?) use anyway. 无论如何,您似乎都不会对继承进行过多的使用。 Even though you've derived your MESSAGE_QUEUE from MESSAGE_LIST (which is actually std::vector<int> ), you embed a pointer to an instance of a MESSAGE_LIST into MESSAGE_QUEUE anyway. 即使你已经派生的MESSAGE_QUEUEMESSAGE_LIST (这实际上是std::vector<int> ),你嵌入一个指向一个实例MESSAGE_LISTMESSAGE_QUEUE反正。

Second, if you're going to use a queue to communicate between threads (which I think is generally a good idea) you should make the locking inherent in the queue operations rather than requiring each thread to manage the locking correctly on its own. 第二,如果你打算使用一个队列中的线程间通信(我认为这是通常一个好主意),你应该在队列操作锁定固有的而不是要求每个线程正确地管理自身的锁定。

Third, a vector isn't a particularly suitable data structure for representing a queue anyway, unless you're going to make it fixed size, and use it roughly like a ring buffer. 第三, vector无论如何都不是用于表示队列的特别合适的数据结构,除非您将其设置为固定大小,并大致像环形缓冲区那样使用它。 That's not a bad idea either, but it's quite a bit different from what you've done. 这也不是一个坏主意,但是与您所做的有很大的不同。 If you're going to make the size dynamic, you'd probably be better off starting with a deque instead. 如果要使大小动态变化,最好从双端队列开始。

Fourth, the magic numbers in your error handling (0x1AE1, 0x1AE2, etc.) is quite opaque. 第四,错误处理中的幻数(0x1AE1、0x1AE2等)非常不透明。 At the very least, you need to give these meaningful names. 至少,您需要给这些有意义的名称。 The one comment you have does not make the use anywhere close to clear. 您所发表的一条评论并未在任何地方都可以使用。

Finally, if you're going to go to all the trouble of writing code for a thread-safe queue, you might as well make it generic so it can hold essentially any kind of data you want, instead of dedicating it to one specific type. 最后,如果您要为编写线程安全队列的代码而烦恼,最好将其设为通用,这样它就可以保存所需的任何类型的数据,而不是将其专用于一种特定类型。

Ultimately, your code doesn't seem to save the client much work or trouble over using the Windows functions directly. 最终,您的代码似乎并没有为客户端节省大量工作或直接使用Windows函数带来的麻烦。 For the most part, you've just provided the same capabilities under slightly different names. 在大多数情况下,您只是以略有不同的名称提供了相同的功能。

IMO, a thread-safe queue should handle almost all the work internally, so that client code can use it about like it would any other queue. 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