信号灯wxwidgets出现异常

Exception with semaphore wxwidgets

本文关键字:异常 wxwidgets 信号灯      更新时间:2023-10-16

我使用WxWidget wiki中的一个示例来了解如何进行线程间和进程间通信。我不知道为什么这个例子有效,但我的代码非常相似,在"m_QueueCount.Wait();//等待信号量(=队列计数变为正)"行不起作用

当我运行代码时,会显示一个exeption:

TeamTranslate.exe中0x00B2DB61处的首次机会异常:0xC0000005:读取位置0x00000078时发生访问冲突。

如果有此异常的处理程序,则程序可能是安全的继续。

断点在xstring文件的第1749行。这是代码:

size_type length() const _NOEXCEPT
        {   // return length of sequence
        return (this->_Mysize);
        }

我希望有人能告诉我为什么这不起作用!

提前谢谢。

标题:

class QueueMSG
{
public:
    QueueMSG(wxEvtHandler* pParent) : m_pParent(pParent) {}
    void AddJob(MessagePTR job); // push a job with given priority class onto the FIFO
    MessagePTR Pop();
    void Report(const Message::tCOMMANDS& cmd, MessagePTR arg); // report back to parent
    size_t Stacksize(); // helper function to return no of pending jobs
private:
    wxEvtHandler* m_pParent;
    std::vector<MessagePTR> m_Jobs; // multimap to reflect prioritization: values with lower keys come first, newer values with same key are appended
    wxMutex m_MutexQueue; // protects queue access
    wxSemaphore m_QueueCount; // semaphore count reflects number of queued jobs
};
class WorkerThread : public wxThread
{
public:
    WorkerThread(QueueMSG* pQueue) : m_pQueue(pQueue) { wxThread::Create(); }
private:
    QueueMSG* m_pQueue;
    BingTranslate bng;
    virtual wxThread::ExitCode Entry();
    virtual void OnJob();
}; // class WorkerThread : public wxThread

cpp:

void QueueMSG::AddJob(MessagePTR job) // push a job with given priority class onto the FIFO
{
    wxMutexLocker lock(m_MutexQueue); // lock the queue
    m_Jobs.push_back(job); // insert the prioritized entry into the multimap
    m_QueueCount.Post(); // new job has arrived: increment semaphore counter
} // void AddJob(const tJOB& job, const tPRIORITY& priority=eNORMAL)
MessagePTR QueueMSG::Pop()
{
    std::vector<MessagePTR>::iterator element;
    m_QueueCount.Wait(); // wait for semaphore (=queue count to become positive)
    m_MutexQueue.Lock(); // lock queue
    element = m_Jobs.begin(); // get the first entry from queue (higher priority classes come first)
    m_Jobs.erase(m_Jobs.begin()); // erase it
    m_MutexQueue.Unlock();// unlock queue
    return *element; // return job entry
} // tJOB Pop()
void QueueMSG::Report(const Message::tCOMMANDS& cmd, MessagePTR arg) // report back to parent
{
    wxThreadEvent evt(wxEVT_THREAD, wxID_ANY);// cmd); // create command event object
    evt.SetPayload<MessagePTR>(arg); // associate string with it
    wxQueueEvent(m_pParent, evt.Clone());
    //m_pParent->AddPendingEvent(evt); // and add it to parent's event queue
} // void Report(const tJOB::tCOMMANDS& cmd, const wxString& arg=wxEmptyString)
size_t QueueMSG::Stacksize()
{
    wxMutexLocker lock(m_MutexQueue); // lock queue until the size has been read
    return m_Jobs.size();
}
void WorkerThread::OnJob()
{
    MessagePTR job = m_pQueue->Pop(); // pop a job from the queue. this will block the worker thread if queue is empty
    bng.translateThis(job);
    switch (job->m_cmd)
    {
    case Message::eID_THREAD_EXIT: // thread should exit
        //Sleep(1000); // wait a while
        throw Message::eID_THREAD_EXIT; // confirm exit command
    case Message::eID_THREAD_JOB: // process a standard job
        //Sleep(2000);
        m_pQueue->Report(Message::eID_THREAD_JOB, job); // report successful completion
        break;
    case Message::eID_THREAD_JOBERR: // process a job that terminates with an error
        m_pQueue->Report(Message::eID_THREAD_JOB, job);
        //Sleep(1000);
        throw Message::eID_THREAD_EXIT; // report exit of worker thread
        break;
    case Message::eID_THREAD_NULL: // dummy command
    default: break; // default
    } // switch(job.m_cmd)
} // virtual void OnJob()    
wxThread::ExitCode WorkerThread::Entry()
{
    Message::tCOMMANDS iErr;
    m_pQueue->Report(Message::eID_THREAD_STARTED, NULL); // tell main thread that worker thread has successfully started
    try {
        while (true)
            OnJob();
    } // this is the main loop: process jobs until a job handler throws
    catch (Message::tCOMMANDS& i) {
        m_pQueue->Report(iErr = i, NULL);
    } // catch return value from error condition
    return (wxThread::ExitCode)iErr; // and return exit code
} // virtual wxThread::ExitCode Entry()

消息类别:

#pragma once

#include <cstring>
#include <stdio.h>
#include <wx/string.h>
#include <vector>
#include <memory>
enum MSGDirection{
    in,
    out
};
class Message {
public:
    enum tCOMMANDS // list of commands that are currently implemented
    {
        eID_THREAD_EXIT = wxID_EXIT, // thread should exit or wants to exit
        eID_THREAD_NULL = wxID_HIGHEST + 1, // dummy command
        eID_THREAD_STARTED, // worker thread has started OK
        eID_THREAD_JOB, // process normal job
        eID_THREAD_JOBERR // process errorneous job after which thread likes to exit
    }; // enum tCOMMANDS
    Message(MSGDirection dir, wxString from, wxString message, wxString language_org, wxString language_dest) : m_message(message), m_dir(dir), m_from(from), m_language_orig(language_org), m_language_dest(language_dest), m_cmd(eID_THREAD_EXIT){
        time_t          rawtime;
        struct tm*      timeinfo;
        char            timestamp[100];
        time(&rawtime);
        timeinfo = localtime(&rawtime);
        strftime(timestamp, 100, "%c", timeinfo);
        m_timestamp = timestamp;
    }
    Message() : m_cmd(eID_THREAD_NULL) {}
    ~Message(){ }
    void setIO(MSGDirection dir);
    MSGDirection getIO(){ return m_dir; };
    void setFrom(char* dir);
    wxString getFrom(){ return m_from; };
    void setMSG(wxString dir){
        m_message = dir;
    };
    wxString getMSG(){ return m_message; }
    wxString getTranslated(){ return m_translated; }
    wxString getTimeStamp(){ return m_timestamp; }
    wxString getLanguageOrig(){ return m_language_orig; }
    wxString getLanguageDest(){ return m_language_dest; }
    void  setSrtTranslate(wxString str){ m_translated = str; }
    tCOMMANDS m_cmd;
private:
    MSGDirection m_dir;
    wxString m_language_orig;
    wxString m_from;
    wxString m_message;
    wxString m_timestamp;
    wxString m_language_dest;
    wxString m_translated;
};

typedef std::shared_ptr<Message> MessagePTR;
typedef std::shared_ptr<std::vector<MessagePTR>> MessageQueuePTR;

QueueMSG::Pop()调用中,即使迭代器无效,也要使用它。即使它没有被对erase的调用所无效(它将是这样的,请参阅此处),您也不能保证在解锁互斥对象后对其进行验证,因为此时您可以进行时间切片,然后另一个线程可以修改容器。因此,您应该改为:

MessagePtr msg;
m_QueueCount.Wait();
{
    wxMutexLocker lock( m_MutexQueue );
    auto element = m_Jobs.begin();
    msg = *element;
    m_Jobs.erase(element);
}
return msg;

在他们的代码中——与您的代码略有不同——他们取消引用迭代器以获得一对,并通过以下行返回包含的第二个元素:

element=(m_Jobs.begin())->second; // that '->'

这就是为什么他们不会遭受不明确的行为。