2011-10-07 47 views
0

我的关键部分代码不起作用!!! Backgrounder.run是否能够修改MESSAGE_QUEUE g_msgQueue和LockSections析构函数尚未被调用!!!C++关键部分不起作用

额外的代码:在一个

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 

回答

3

两个问题。我不知道第一个,但关键部分很容易解释。后台线程并不想要锁,因此当然不会被阻止。您需要使关键部分对象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

+0

感谢大卫。我修改了帖子,所以现在是一个问题,最重要的一个问题。我想这意味着我必须在Backgrounder :: Run中使用LockSection! –

+0

我现在有这样的代码:if(void *的)参数表是2点的指针,你如何提取它们的每一个?例如_beginthread(运行,0,(无效*)(m_pMsgQueue,m_pLc)); ... static void __cdecl Run(void * args){p1 = arg1; p2 = arg2; }? –

+0

这是一个不同的问题。在评论中无法真正解决它。随意问一个新问题。 –

1

您试图在弹出后返回最后一个元素

2

你的后台线程需要访问同一个CriticalSection对象,它需要创建LockSection对象来锁定它 - 锁定是协作的。

3

此代码有一些问题。

首先,从标准容器派生几乎总是一个不好的主意。在这种情况下,您正在使用私有继承,这可以减少问题,但不会完全消除它们。无论如何,你似乎并没有把继承放在很多(任何?)的地方。即使你已经从MESSAGE_LIST(它实际上是std::vector<int>)派生出MESSAGE_QUEUE,但是无论如何,都会将指向MESSAGE_LIST的实例的指针嵌入到MESSAGE_QUEUE中。其次,如果你打算用一个队列来进行线程之间的通信(我认为一般是个好主意),你应该在队列操作中使锁定固有,而不是要求每个线程来管理锁定正确地在自己。

第三,一个vector不是一个特别适合表示队列的数据结构,除非你打算使它成为固定大小,并且大致像环形缓冲区那样使用它。这也不是一个坏主意,但它与你所做的完全不同。如果你打算让大小变化,那么你可能会更好地从一个deque开始。第四,错误处理(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; 
} 
+0

+1 - 今年,我已经被迫这两次同意的躺椅和给予好评:( –