【问题标题】:wxWidgets wxThreadHelper Example QuestionwxWidgets wxThreadHelper 示例问题
【发布时间】:2021-07-17 04:15:15
【问题描述】:

我对 wxWidgets 比较陌生,并试图掌握如何正确实现线程和事件以及所有这些好东西。我能够在我的项目目录中运行一个简单的框架,但在尝试添加线程时遇到了问题。

我运行 repo 的“示例”中给出的 Thread 演示没有问题,但据我了解,最近的首选方法是使用 wxThreadHelper,因为您不需要传递帧指针。

我基本上是从here 给出的 wxThreadHelper 示例开始的。我真正做的只是将内容分成 header 和 src。

标题:

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#if !wxUSE_THREADS
    #error "This sample requires thread support!"
#endif // wxUSE_THREADS


wxDECLARE_EVENT(EVT_COMMS_UPDATE, wxThreadEvent);

enum {LAYOUT_TEST_PROPORTIONS = wxID_HIGHEST+1,
        LAYOUT_TEST_SIZER,
        LAYOUT_TEST_NB_SIZER
    };


class wxMainFrame : public wxFrame, public wxThreadHelper, private wxLog {
private:

public:
    wxMainFrame();
    ~wxMainFrame(){
        // it's better to do any thread cleanup in the OnClose()
        // event handler, rather than in the destructor.
        // This is because the event loop for a top-level window is not
        // active anymore when its destructor is called and if the thread
        // sends events when ending, they won't be processed unless
        // you ended the thread from OnClose.
        // See @ref overview_windowdeletion for more info.
    }
    void onCommsUpdate_t();
    void OnClose(wxCloseEvent& evt);
    
    wxTextCtrl * m_txtctrl;
    
    
protected:
    virtual wxThread::ExitCode Entry();
    
    //fields update from threads
    double data_t;
    wxCriticalSection m_dataCS; // protects field above - i don't really know how this thing scopes the CS
    
    wxDECLARE_EVENT_TABLE();

};

SRC:

#include "wxMainFrame.h"

wxDEFINE_EVENT(EVT_COMMS_UPDATE, wxThreadEvent)
wxBEGIN_EVENT_TABLE(wxMainFrame, wxFrame)
    EVT_THREAD(EVT_COMMS_UPDATE, wxMainFrame::onCommsUpdate_t)
    EVT_CLOSE(wxMainFrame::OnClose)
wxEND_EVENT_TABLE()

wxMainFrame::wxMainFrame() : wxFrame(NULL, wxID_ANY, "wxWidgets Layout Demo") {
    
    //Enable thread
    if (CreateThread(wxTHREAD_JOINABLE) != wxTHREAD_NO_ERROR){
        wxLogError("Could not create the worker thread!");
        return;
    }
    // go!
    if (GetThread()->Run() != wxTHREAD_NO_ERROR) {
        wxLogError("Could not run the worker thread!");
        return;
    }
    
    this->data_t = 0;
    
    // Make a menubar
    wxMenu *file_menu = new wxMenu;

    file_menu->Append(LAYOUT_TEST_PROPORTIONS, "&Proportions demo...\tF1");
    file_menu->Append(LAYOUT_TEST_SIZER, "Test wx&FlexSizer...\tF2");
    file_menu->Append(LAYOUT_TEST_NB_SIZER, "Test &notebook sizers...\tF3");
    wxMenuBar *menu_bar = new wxMenuBar;

    menu_bar->Append(file_menu, "&File");

    // Associate the menu bar with the frame
    SetMenuBar(menu_bar);
    
    

    
    // create the logging text control and a header showing the meaning of the
    // different columns
    wxTextCtrl *header = new wxTextCtrl(this, wxID_ANY, "",
                                        wxDefaultPosition, wxDefaultSize,
                                        wxTE_READONLY);
    //DoLogLine(header, "  Time", " Thread", "Message");
    m_txtctrl = new wxTextCtrl(this, wxID_ANY, "",
                               wxDefaultPosition, wxDefaultSize,
                               wxTE_MULTILINE | wxTE_READONLY); 
    
    wxLog::SetActiveTarget(this);

    // use fixed width font to align output in nice columns
    wxFont font(wxFontInfo().Family(wxFONTFAMILY_TELETYPE));
    header      ->SetFont(font);
    m_txtctrl   ->SetFont(font);

    m_txtctrl->SetFocus();  //Field to have focus on startu

    // layout and show the frame
    wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
    sizer->Add(header, wxSizerFlags().Expand());
    sizer->Add(m_txtctrl, wxSizerFlags(1).Expand());    
    SetSizer(sizer);

    SetSize(600, 350);
    
}

wxThread::ExitCode wxMainFrame::Entry(){
    // IMPORTANT: this function gets executed in the secondary thread context!
    while (!GetThread()->TestDestroy()) {
        // since this Entry() is implemented in MyFrame context we don't
        // need any pointer to access the m_data, m_processedData, m_dataCS
        // variables... very nice!
        wxCriticalSectionLocker lock(m_dataCS);
        
        this->data_t++;
        
        // VERY IMPORTANT: do not call any GUI function inside this
        //                 function; rather use wxQueueEvent():
        wxQueueEvent(this, new wxThreadEvent(EVT_COMMS_UPDATE));
    }
    
    // TestDestroy() returned true (which means the main thread asked us
    // to terminate as soon as possible) or we ended the long task...
    return (wxThread::ExitCode)0;
}

void wxMainFrame::onCommsUpdate_t(){
    //Do thread stuff
    //wxCriticalSectionLocker lock(m_dataCS);
    //std::cout << this->data_t;
}

void wxMainFrame::OnClose(wxCloseEvent& evt){
    // important: before terminating, we _must_ wait for our joinable
    // thread to end, if it's running; in fact it uses variables of this
    // instance and posts events to *this event handler
    if (GetThread() &&      // DoStartALongTask() may have not been called
        GetThread()->IsRunning())
        GetThread()->Wait();
    Destroy();
}

这会导致这个编译错误:

In file included from /usr/local/include/wx-3.1/wx/wx.h:24,
                 from includes/wxMainFrame.h:10,
                 from src/wxMainFrame.cpp:2:
/usr/local/include/wx-3.1/wx/event.h:4350:5: error: expected ‘,’ or ‘;’ before ‘                                                            const’
 4350 |     const wxEventTable theClass::sm_eventTable = \
      |     ^~~~~
src/wxMainFrame.cpp:5:1: note: in expansion of macro ‘wxBEGIN_EVENT_TABLE’
    5 | wxBEGIN_EVENT_TABLE(wxMainFrame, wxFrame)

如您所见,这是一个基本语法错误,如果我进入 /usr/local/include/wx-3.1/wx/event.h:4350,我可以看到/修复它

但这没有意义,我不能是第一个发现简单语法错误的人吗?我的机器(ubuntu)上一定有问题。这是从make install 添加到我的系统包含的标头,对吗?如果从源代码构建的演示/示例都可以正常工作,将如何出现语法错误。我想只是大声思考。

我使用here 的建议从源代码构建了该库。

我还在 wxWidget 的论坛here 上看到了这篇文章。谁似乎没有我遇到的相同语法问题。而且,我并不真正理解如何使用建议的宏,因为 wiki 说宏实际上是由 wx__DECLARE_EVT2 包装的。如果这是解决方案,也许一个简单的示例将有助于向我展示如何正确使用它。

感谢任何意见。谢谢!

PS 我已经尝试在论坛上发布此问题,但无法注册(尝试使用多个浏览器)所以我在这里。

【问题讨论】:

    标签: c++ wxwidgets


    【解决方案1】:

    这里有很多问题。首先,您提到的编译错误是由于缺少分号,行:

    wxDEFINE_EVENT(EVT_COMMS_UPDATE, wxThreadEvent)
    

    应该是

    wxDEFINE_EVENT(EVT_COMMS_UPDATE, wxThreadEvent);
    

    二、onCommsUpdate_t方法需要带一个wxThreadEvent&参数

    void onCommsUpdate_t(wxThreadEvent&);
    

    显然,方法的定义也需要更新。


    第三,老实说,我不知道 EVT_THREAD 宏应该如何工作,但我知道这条线

    EVT_THREAD(EVT_COMMS_UPDATE, wxMainFrame::onCommsUpdate_t)
    

    不起作用。处理线程事件最简单的方法是使用Bind 方法。在框架构造函数中添加这样一行来处理事件

    Bind(EVT_COMMS_UPDATE,&wxMainFrame::onCommsUpdate_t,this);
    

    第四,如前所述,您的wxMainFrame::Entry 方法将不断向事件队列发送垃圾邮件,从而冻结应用程序。在实际应用中,大概线程会在对每个事件进行排队之前做一些工作。因为在这个例子中,没有做任何工作,你应该添加一行像

    wxMilliSleep(500);
    

    到while循环的末尾,让线程在发送每个事件之前休眠一点。


    第五,您从来没有真正向您的线程发出关闭信号,它也永远不会自行关闭。因此,GetThread()-&gt;Wait(); 行将是一个无限循环。有很多方法可以向线程发出信号,但最简单的方法可能是将名为 m_shutdown 之类的 bool 成员添加到框架类中。那么

    一个。在框架构造函数中将m_shutdown 初始化为false。

    b.在wxMainFrame::OnClose中,在调用Wait之前使用临界区设置为true:

    if (GetThread() &&      // DoStartALongTask() may have not been called
        GetThread()->IsRunning())
    {
        {
            wxCriticalSectionLocker lock(m_dataCS);
            m_shutdown = true;
        }
        
        GetThread()->Wait();
    }
    

    c。在wxMainFrame::Entry 方法中,在while 循环中添加对m_shutdown 值的检查(由临界区保护)。

        while (!GetThread()->TestDestroy()) {
            // since this Entry() is implemented in MyFrame context we don't
            // need any pointer to access the m_data, m_processedData, m_dataCS
            // variables... very nice!
            {
                wxCriticalSectionLocker lock(m_dataCS);
                this->data_t++;
                if ( m_shutdown )
                    break;
            }
    ...
    

    第六,不知道是什么线

    wxLog::SetActiveTarget(this);
    

    应该这样做,但它会导致程序退出时崩溃。如果需要,您需要存储初始日志目标并将其恢复到帧析构函数或关闭事件处理程序中。

    【讨论】:

    • 哇,这里有很多很好的信息。比我预期的更多问题。我添加了您建议的更改并消除了原始错误,但我遇到了一个新问题:../include/wx/thrimpl.cpp(40): assert (m_internal) failed in Lock(): wxMutex::Lock(): not initialized [in thread ffffafc0ee70]。据我了解,互斥锁应该隐含在wxCriticalSection 声明中,对吗?
    • 啊忘了包含#include &lt;wx/thread.h&gt;。现在似乎工作得很好!后续问题,您似乎精通 wxWidgets,使用 wxThreadHandler 运行多个线程的首选方法是什么?这可能吗?似乎它可能由多个空帧组成,这听起来并不理想。也许在这种情况下建议避免使用 wxThreadHandler 并坚持使用正常的 wxThread 实现方法?
    • 如你所说,wxThreadHelper 只有一个线程。如果您需要多个线程,则需要自己制作线程。
    • 只是一个旁注,第一个问题可能源于文档中存在相同的错字:docs.wxwidgets.org/3.0/classwx_thread.html
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-23
    • 1970-01-01
    • 2021-05-08
    • 2015-08-30
    • 2010-11-09
    • 1970-01-01
    相关资源
    最近更新 更多