在前一个线程返回后才开始一个新的`wxThread`

时间:2018-01-05 17:05:56

标签: c++ multithreading wxwidgets

案例1: 我正在使用wxThreads,我使用2个for循环创建线程。我有一个MyThreads类,它继承自wxThread类。此外,每个线程在退出之前创建wxThreadEvent并将数据发送到主程序。每个线程完成后,主程序执行DoThisWorkAfterThreadReturns()。我想要做的是,所有level = 0的线程都可以同时执行。但是在使用level = 1创建线程之前,所有0级线程都应该已经完成​​执行,并且所有DoThisWorkAfterThreadReturns()线程的level 0执行也应该完成。我应该如何使用wxWidgets

执行此操作
for(level=0;level<n;level++)
{
     for(int i=0;i<no;i++)
     {
          //threads in this loop can execute simultaneously.
          MyThread *thread = new MyThread(this);
          thread->create();
          thread->run();
     }
     //wait till all threads for given level finish execution and execute 
      DoThisWorkAfterThreadReturns()
}

案例2: 如果CASE 1不可能,那么我可以关注吗?

  for(i=0;i<n;i++)
  {
       MyThread *thread = new MyThread(this);
       thread->create();
       thread->run();
       // wait till this thread finishes its execution, returns data to main program and main program finishes execution of DoThisWorkAfterThreadReturns()
      // after this only execute i++(i.e. next thread)
  }

我可以在从for循环创建新线程之前等待每个线程完成吗?我发送后端请求时需要创建线程,这有时需要很长时间。

2 个答案:

答案 0 :(得分:0)

我不认为这可以简单地完成。这是一个实现方法的一个例子。我使用带有文本控件和按钮的简单应用程序。为了完成上面描述的内容,我将工作分为3个功能。

第一个函数启动外循环的工作。在这个例子中,它是按钮的事件处理程序。

第二个函数产生许多线程,基本上对应于上面描述的内部循环。这个例子中的线程很愚蠢。他们只是等待0到5秒之间的随机时间,抛出一个事件宣布他们已经完成,然后自行删除。

第三个函数是每个线程完成时调用的线程事件处理程序。它基本上完成了外循环的工作。它会检查运行的线程数,如果为零,它将启动内部循环的新迭代或完成。

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

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

// 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

#include <wx/thread.h>
#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */

class MyThread : public wxThread
{
    public:
        MyThread(wxEvtHandler *handler,int sleeptime)
            : wxThread(wxTHREAD_DETACHED)
            { m_pHandler = handler;m_sleepTime= sleeptime;}
    protected:
        virtual ExitCode Entry();

        wxEvtHandler *m_pHandler;
        int m_sleepTime;
};

wxThread::ExitCode MyThread::Entry()
{
    // A real application would do something here,
    // but for this example the only thing done is sleeping
    Sleep(m_sleepTime);

    // The work is done.  Throw a thread event to announce this to the frame.
    wxThreadEvent* exitEvent = new  wxThreadEvent();
    exitEvent->SetInt(m_sleepTime);
    wxQueueEvent(m_pHandler, exitEvent);
    return (wxThread::ExitCode)0;
}

class MyFrame : public wxFrame
{
    public:
        MyFrame( wxWindow* parent, int id = wxID_ANY, wxString title = "Demo",
                 wxPoint pos = wxDefaultPosition, wxSize size = wxSize(481,466),
                 int style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
    private:
        void OnButton(wxCommandEvent& event);
        void OnThreadComplete(wxThreadEvent& event);
        void SpawnThreads();

        int m_outerLoopCounter;
        int m_outerLoopLimit;
        int m_threadsToUse;

        wxCriticalSection m_threadsRunningCS;
        int m_threadsRunning;

        wxTextCtrl* m_textCtrl;
        wxButton* m_button;
};

MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos
                 , wxSize size, int style )
        :wxFrame( parent, id, title, pos, size, style )
{
    wxPanel* panel = new wxPanel(this);
    wxBoxSizer* szr = new wxBoxSizer( wxVERTICAL );

    m_textCtrl = new wxTextCtrl( panel, wxID_ANY, wxEmptyString,
                                 wxDefaultPosition, wxDefaultSize,
                                 wxTE_DONTWRAP|wxTE_MULTILINE );
    szr->Add( m_textCtrl, 1, wxALL|wxEXPAND, 5 );

    m_button = new wxButton( panel, wxID_ANY, "Spawn");
    szr->Add( m_button, 0, wxALL, 5 );

    panel->SetSizer( szr );
    Layout();

    srand(time(NULL));
    m_outerLoopLimit = 3;
    m_threadsToUse = 4;

    Bind( wxEVT_THREAD, &MyFrame::OnThreadComplete, this);
    m_button->Bind( wxEVT_BUTTON, &MyFrame::OnButton, this );
}

void MyFrame::OnButton(wxCommandEvent& event)
{
    m_button->Disable();
    m_outerLoopCounter=0;
    SpawnThreads();
}

void MyFrame::SpawnThreads()
{
    (*m_textCtrl) << "spawning threads for loop " << m_outerLoopCounter+1;
    (*m_textCtrl) << " of " << m_outerLoopLimit <<"\n";

    m_threadsRunning=0;
    for ( int i=0; i<m_threadsToUse; ++i )
    {
        int sleeptime = rand()%5000;
        (*m_textCtrl) << "\tthread " << i << " will sleep for ";
        (*m_textCtrl) << sleeptime << " ms.\n";
        MyThread* thread = new MyThread(this,sleeptime);

        wxCriticalSectionLocker enter(m_threadsRunningCS);
        ++m_threadsRunning;

        if ( thread->Run() != wxTHREAD_NO_ERROR )
        {
            wxLogError("Can't create the thread!");
            delete thread;
            --m_threadsRunning;
        }
    }
}

void MyFrame::OnThreadComplete(wxThreadEvent& event)
{
    (*m_textCtrl) << "\tThe thread that slept for ";
    (*m_textCtrl) << event.GetInt() << " ms has finished.\n";

    // Check the number of threads that are still running
    bool canStop = false;
    {
        wxCriticalSectionLocker enter(m_threadsRunningCS);
        --m_threadsRunning;
        if ( m_threadsRunning == 0 )
        {
            canStop=true;
        }
    }

    // If there are zero threads still running, either enter a new iteration
    // of the outer loop or stop if the outer loop is complete.
    if(canStop)
    {
        ++m_outerLoopCounter;
        if ( m_outerLoopCounter<m_outerLoopLimit )
        {
            SpawnThreads();
        }
        else
        {
            (*m_textCtrl) << "All Done.\n";
            m_button->Enable();
        }
    }
}

class MyApp : public wxApp
{
    public:
        virtual bool OnInit()
        {
            MyFrame* frame = new MyFrame(NULL);
            frame->Show();
            return true;
        }
};

wxIMPLEMENT_APP(MyApp);

这里有一些缺点。外循环计数器和运行的线程数需要使用多个函数中可用的变量进行跟踪。所以他们需要是全局变量或框架或app类的成员。另外,我认为运行线程数的变量应该用一个关键部分来保护。 (我可能错了,但在上面的例子中,我决定安全并使用关键部分。)

可能有一种更简单的方法可以做到这一点。这是我尝试过的第一件事。

答案 1 :(得分:0)

wxSemaphore是一个反击。如果内部计数器为零,则wxSemaphore::Wait()等待,否则它会递减计数器并返回。

你需要与wxSemaphore相反,等待计数器为零的东西。
设置一个var(让我们称之为tVar),它在线程开始时递增,在线程结束时递减。

class MyThread : public wxThread
{
    ....
    MyThread(someObject *obj, wxEvtHandler *evtH) //someObject is where tVar lives, evtH is where we will post an event
    { m_obj = obj;  m_evtH = evtH;}
    ....
    someObject *m_obj;
    wxEvtHandler *m_evtH;
};

wxThread::ExitCode MyThread::Entry()
{
    //Increment the var
    wxCriticalSection locker(m_obj->someCritSec) //someCritSec must be accessible in someObject
    locker.Enter();
    m_obj->IncrementVar();
    locker.Leave() //Allow other threads to increment the var while this thread is still working

   //do the task
   .....

   //and decrement through a message
    wxQueueEvent(m_evtH, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_COMPLETED));

    return 0;
}

someObject派生自wxEvtHandler(例如,wxWindow),因此它可以接收消息。通过事件表或更好的Bind(),你有一个线程完成事件的处理程序:

void someObject::OnThreadCompleted(wxThreadEvent&)
{
    //decrement the var
    --tVar;
    //Do something when it reaches 0
    if ( tVar == 0 )
        DoThisWorkAfterThreadReturns();
}

此解决方案允许GUI在线程正常工作时响应。

真的没有人在等待。这是DoThisWorkAfterThreadReturns仅在所有线程完成时执行。 “等级”必须“等待”与否的逻辑是你的决定。

此解决方案中有一个小警告:如果第一个创建的线程在另一个线程开始运行之前完成,则将发布该消息,并且有可能在另一个线程增加var之前调用其处理程序。
你可以在创建任何线程之前使用tVar = 1来避免它,并在创建最后一个线程之后立即减少它(发布一个事件)。