只有在上一个线程返回后才启动一个新的"wxThread"

start a new `wxThread` only after previous thread returns

本文关键字:quot 一个 wxThread 上一个 线程 返回 启动      更新时间:2023-10-16

情况 1: 我正在使用wxThreads,我正在使用 2 个for循环创建线程。我有一个从wxThread类继承而来的MyThreads类。此外,每个线程在退出之前创建一个wxThreadEvent并将数据发送到主程序。主程序在每个线程完成后执行DoThisWorkAfterThreadReturns()。我想做的是,level=0 的所有线程都可以同时执行。但是在使用level = 1创建线程之前,所有 0 级线程都应该完成它们的执行,并且所有level 0线程的DoThisWorkAfterThreadReturns()执行也应该完成。我应该如何使用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: 如果无法出现案例 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 循环创建新线程吗?有必要创建线程,因为我正在发送后端请求,这有时需要很长时间。

我认为这不能简单地完成。 这是一种方法的一个例子。 我正在使用带有文本控件和按钮的简单应用程序。 为了完成您上面描述的内容,我将工作分为 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);

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

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

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,并在创建最后一个线程后立即递减它(发布事件)来避免它。