简体   繁体   English

仅在先前线程返回后才开始新的wxThread

[英]start a new `wxThread` only after previous thread returns

CASE 1: I am using wxThreads , I am creating threads using 2 for loops. 案例1:我正在使用wxThreads ,我正在使用2 for循环创建线程。 I have a MyThreads class which is inherited from wxThread class. 我有一个MyThreads类,它是从wxThread类继承的。 Also, every thread creates a wxThreadEvent before exit and sends data to main program. 同样,每个线程在退出之前都创建一个wxThreadEvent并将数据发送到主程序。 Main program executes DoThisWorkAfterThreadReturns() after every thread is finished. 每个线程完成后,主程序将执行DoThisWorkAfterThreadReturns() what I want to do is, all threads with level=0 can execute simultaneously. 我想做的是,所有级别为0的线程都可以同时执行。 But before creating threads with level = 1 , all level 0 threads should have finished their execution and DoThisWorkAfterThreadReturns() execution for all level 0 threads should also be finished. 但是在创建level = 1线程之前,所有级别0的线程都应该完成其执行,并且还应该完成所有level 0线程的DoThisWorkAfterThreadReturns()执行。 How should I do this using wxWidgets ? 我应该如何使用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()
}

CASE 2: If CASE 1 is not possible then can I do following ? 案例2:如果案例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)
  }

can I wait for every thread to finish before creating a new thread from for loop ? 在for循环中创建新线程之前,我可以等待每个线程完成吗? It is necessary to create threads as I am sending backend requests which takes a long time sometimes. 在发送后端请求时,有必要创建线程,这有时会花费很长时间。

I don't think this can be done simply. 我认为这不能简单地完成。 Here's one example of a way to do it. 这是一种实现方法的示例。 I'm using a simple application with a text control and a button. 我正在使用一个带有文本控件和按钮的简单应用程序。 To accomplish what you have described above, I've split the work between 3 functions. 为了完成上述工作,我将工作分为3个功能。

The first function starts the work of the outer loop. 第一个功能开始外循环的工作。 In this example, it's an event handler for the button. 在此示例中,它是按钮的事件处理程序。

The second function spawns a number of threads and basically corresponds to the inner loop described above. 第二个函数产生许多线程,并且基本上对应于上述内部循环。 The threads in this example are dumb. 本示例中的线程是哑的。 They simply wait for a random amount of time between 0 and 5 seconds, throw an event to announce they are done, and then delete themselves. 他们只是等待0到5秒之间的随机时间,抛出一个事件来宣布已完成,然后删除自己。

The third function is a thread event handler called when each thread is finished. 第三个函数是每个线程完成时调用的线程事件处理程序。 It basically finishes the work of the outer loop. 它基本上完成了外循环的工作。 It checks the number of threads running, and if that is zero it will either start a new iteration of the inner loop or finish. 它检查正在运行的线程数,如果该数目为零,它将开始内部循环的新迭代或结束。

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

There are some downsides here. 这里有一些缺点。 The outer loop counter and the number of threads running need to be tracked with variables available in multiple functions. 需要使用多个函数中可用的变量来跟踪外循环计数器和正在运行的线程数。 So they need to be either global variables or members of the frame or app class. 因此,它们需要是全局变量,或者是框架或应用程序类的成员。 Also, I think the variable for the number of running threads should be guarded with a critical section. 另外,我认为运行线程数的变量应该用关键部分加以保护。 (I could be wrong, but in the example above, I decided to be safe and used the critical section.) (我可能是错的,但是在上面的示例中,我决定安全并使用关键部分。)

There may be a simpler way to do this. 可能有一种更简单的方法来执行此操作。 It's just the first thing I've tried. 这只是我尝试过的第一件事。

wxSemaphore is a counter. wxSemaphore是一个计数器。 wxSemaphore::Wait() waits if the internal counter is zero, otherwise it decrements the counter and returns. 如果内部计数器为零,则wxSemaphore::Wait()等待,否则它递减计数器并返回。

You need the opposite to wxSemaphore , something that waits until the counter is zero. 您需要与wxSemaphore相反的东西,它要等到计数器为零。
Set a var (let's call it tVar ) that increments when the thread starts and decrements when the thread ends. 设置一个tVar (我们称其为tVar ),该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;
}

Class someObject is derived from wxEvtHandler (for example, a wxWindow) so it can receive the message. someObject类是从wxEvtHandler (例如wxWindow)派生的,因此它可以接收消息。 By an event-table or better by Bind() you have a handler for the thread completion event: 通过一个事件表或者通过Bind()更好,您有一个用于线程完成事件的处理程序:

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

This solution allows the GUI to be responsive while threads are working. 此解决方案允许GUI在线程工作时响应。

Really no body is waiting. 真的没有人在等待。 It's that DoThisWorkAfterThreadReturns is only executed when all threads have finished. 只有在所有线程完成后才执行DoThisWorkAfterThreadReturns The logic on which "levels" must "wait" or not is your decision. “级别”必须“等待”或不“等待”的逻辑是您的决定。

There is a small caveat in this solution: if the first created thread finishes before another thread begins running the message will be posted and there's a chance that its handler is called before another thread increments the var. 此解决方案有一个小警告:如果第一个创建的线程在另一个线程开始运行之前完成,则消息将被发布,并且有可能在另一个线程递增var之前调用其处理程序。
You can avoid it by having tVar = 1 before any thread is created, and decrementing it (post an event) right after the last thread is created. 您可以通过在创建任何线程之前使tVar = 1在创建最后一个线程之后立即将其递减(发布事件)来避免这种情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM