wxWidget教程(7)——多线程、同步、定时器与空闲处理

时间:2021-04-19 00:10:50

一、先来看个简单的例子:

新建MyThread,继承wxThread

#pragma once
#include <wx/dialog.h>
#include <wx/thread.h>
#include <wx/event.h>

wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxThreadEvent);
wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent);

// 为线程事件传输数据,你可以定义成其他的结构
typedef struct THREAD_MSG
{
	DWORD threadId;
	wxString info;
} THREAD_MSG_TYPE;

class MyThread :
	public wxThread
{
public:
	MyThread(wxDialog* handler);
	~MyThread();

	virtual void * Entry() override;
	void SendMsgToDialog(const wxEventType & evtType, const wxString& msg);

protected:
	wxDialog * m_handler;
};

#include "MyThread.h"

wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxThreadEvent);
wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent);

MyThread::MyThread(wxDialog* handler)
{
	m_handler = handler;
}

MyThread::~MyThread()
{
}

void * MyThread::Entry()
{
	if (!TestDestroy()) {
		SendMsgToDialog(wxEVT_COMMAND_MYTHREAD_UPDATE, wxT("Thread is running!"));
		// ...
	}
	SendMsgToDialog(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxT("Thread is over!"));
	return 0;
}

void MyThread::SendMsgToDialog(const wxEventType & evtType,const wxString& msg) {
	wxThreadEvent * threadEvt = new wxThreadEvent(evtType);
	THREAD_MSG_TYPE * threadMsg = new THREAD_MSG_TYPE{ wxThread::GetCurrentId(),msg };
	threadEvt->SetExtraLong(reinterpret_cast<long>(threadMsg));
	wxQueueEvent(m_handler, threadEvt);
}

在对话框MyDialog中调用如下:

#pragma once  
#include "GUI.h"  
#include <wx/msgdlg.h> 
#include "MyThread.h"

class MyDialog :public MainDialog
{
public:
	MyDialog(wxDialog * dlg) :MainDialog(dlg) {}
	~MyDialog() {}
	void OnThreadUpdate(wxCommandEvent& evt);
	void OnThreadCompletion(wxCommandEvent& evt);
	void OnStart(wxCommandEvent& event);
protected:
	MyThread * pThread;
	wxDECLARE_EVENT_TABLE();
};
#include "MyDialog.h" 
#include "vld.h"

wxBEGIN_EVENT_TABLE(MyDialog, MainDialog)
	EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_UPDATE, MyDialog::OnThreadUpdate)
	EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_COMPLETED, MyDialog::OnThreadCompletion)
wxEND_EVENT_TABLE()

void MyDialog::OnThreadUpdate(wxCommandEvent& evt) {
	wxThreadEvent* tEvt = (wxThreadEvent*)&evt;
	THREAD_MSG_TYPE * threadMsg = (THREAD_MSG_TYPE *)tEvt->GetExtraLong();
	wxString msg = wxString::Format(wxT("线程Id:%x,发来消息:%s\r\n"), threadMsg->threadId, threadMsg->info);
	OutputDebugString(msg);
	delete threadMsg;
}
void MyDialog::OnThreadCompletion(wxCommandEvent& evt) {
	wxThreadEvent* tEvt = (wxThreadEvent*)&evt;
	THREAD_MSG_TYPE * threadMsg = (THREAD_MSG_TYPE *)tEvt->GetExtraLong();
	wxString msg = wxString::Format(wxT("线程Id:%x,发来消息:%s\r\n"), threadMsg->threadId, threadMsg->info);
	OutputDebugString(msg);
	delete threadMsg;
	pThread = nullptr;
}
void MyDialog::OnStart(wxCommandEvent& event)
{
	pThread = new MyThread(this);
	pThread->Run();
}


二、线程类wxThread

1、创建线程,指定线程是detach还是join,join需要等待,detach立即返回

MyThread::MyThread(wxDialog* handler):wxThread(wxTHREAD_DETACHED)
{
	m_handler = handler;
}

可以通过GetKind函数判断是哪种线程


2、需不需要调用create函数

一般情况下,不需要调用create函数,因为在run中会调用一次

	pThread = new MyThread(this);
	if (pThread->Run() != wxTHREAD_NO_ERROR) {
		wxLogError(wxT("Thread create error."));
	}


3、线程挂起、启动与终止

	pThread->Pause();
	pThread->Resume();
	pThread->Kill();

可以通过IsAlive来判断线程是否还活着


4、设置线程的优先级,范围0-100,wxPRIORITY_MIN,wxPRIORITY_DEFAULT,wxPRIORITY_MAX

	pThread->SetPriority(wxPRIORITY_MAX);


5、线程睡眠与谦让

	wxThread::Sleep(1000);
	wxThread::Yield();

三、线程同步

1、wxMutex互斥量,某个时刻,多个线程只能有一个拥有这个互斥量

	wxMutex m_mutex;
	wxMutexLocker lock(m_mutex);
2、wxCriticalSection关键区域。某个时刻,多个线程只能有一个执行关键区域的代码

	wxCriticalSection cs;
	cs.Enter();
	// ...
	cs.Leave();
3、wxSemaphore信号量,初始时,可以设置起始信号数,和最大的信号数

	wxSemaphore semaphore(2,10);
	semaphore.Wait();
	// ...
	semaphore.Post();
4、wxCondition条件变量,一般用在生产者消费者模式中

	// 创建一个条件变量
	wxCondition condition(m_mutex);
	// 让条件变量处于等待状态
	condition.Wait();
	condition.WaitTimeout(1000);
	// 通知所有线程中的条件变量,有信号了
	condition.Broadcast();
	// 通知最多一个线程的条件变量,有信号了
	condition.Signal();


四、定时器wxTimer

1、创建一个定时器

#define TIMER_ID	1000

wxTimer m_timer(this, TIMER_ID);
// 事件
EVT_TIMER(TIMER_ID, MyDialog::OnTimer)
2、开始与停止

	m_timer.StartOnce(2000);
	m_timer.Start(2000);
	m_timer.Stop();

五、空闲时间,当CPU处于空闲的时候会执行

1、设置空间时间处理事件

	EVT_IDLE(MyDialog::OnMyIdle)
2、事件处理函数

void OnMyIdle(wxIdleEvent& event);
3、判断事情有没有处理完,没有则再次请求

	if (!FinishedIdleTask()) {
		event.RequestMore();
	}
	event.Skip();