大家好,本人是C++新人qing。
我学习编程也快十年了,这一年来我用C++写了一些程序,有了一些新奇的想法。
我写了一些诸如“C语言存储变长字符串”、“C++可控制线程对象”、“TCP通信接收任意长度字符串”的代码。
这些都是我的拙作,希望能够分享给大家,主要是新人可以练练手,有意见也可以提。
我现在还没有验证这些代码,等我回家以后我在电脑上弄一个可以运行的事例给大家。
----------------------------------------------------------------------------------------
创建状态机类
我刚学习线程的时候,认为线程是主线程的一种扩展,所以把线程当成了另一个分支来用,造成了线程的目标函数比较复杂,而我想让它在我想要的时候自主退出。
为了解决对线程的控制问题,我没有想到用协程,而是在线程中集成了一个类似状态机的东西。
状态机的要求:
1.内部包含至少4种状态,分别对应线程的终止、静止、开始和运行。
2.可以检查或者改变状态。
2.线程安全
以下是状态机类的声明和实现,代码还未检查:
//fsm.h
#pragma once
#include<mutex>
namespace qing{
//用来存储状态的枚举类型
enum Stat{
SSHUT = -1,
SSTOP = 0,
SSTART = 1,
SRUNNING = 2
};//Status
class Fsm{//FiniteStateMachine有限状态机
public:
//构造函数
Fsm();
//检查状态
enum Stat chk();
//改变状态
void wake();
void stop();
void close();
void run();
private:
//锁类型,确保状态机线程安全
std::mutex lock;
//存放状态机的状态
enum Stat stat;
};//StateMachine
}//qing
//fsm.cpp
#include"fsm.h"
namespace qing{
Fsm::Fsm(){
this->stat = SSTOP;
}
//------------------------------------------------------
enum Stat Fsm::chk() {
std::unique_lock<std::mutex> lck(this->lock);
return this->stat;
}//check
//-------------------------------------------------------
void Fsm::wake() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SSTART;
}//wake
void Fsm::stop() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SSTOP;
}//stop
void Fsm::close() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SSHUT;
}//close
void Fsm::run() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SRUNNING;
}//run
}//qing
可以看到我写的这个状态机类中包含了一个锁用来确保线程安全。
我们在使用时只需要继承这个类就可以了喵。
-----------------------------------------------------------------------------------------
构建目标函数和事件接口
接下来我们让我们创建一个线程的目标函数,这个目标函数我受到Arduino的启发,把它写成了一个循环,直到状态机处于SHUT状态之前,它会重复地执行某些事情。
在这个主循环中,会涉及到4个触发事件,分别是“等待”事件、“唤醒”事件、“循环体”以及”清除“事件,这四个函数因为需要用到资源,所以在继承的时候写在类里。
主循环的要求:
1.在SHUT状态之前,保持循环状态。
2.在STOP状态时,持续地进入“等待”事件。
3.在START状态时,执行一次“唤醒”事件(唤醒成功则会进入RUNNING运行态,失败则进入STOP状态)
4.从任何其他状态转变为STOP、SHUT态时,执行一次“清理”事件。
以下是代码,还未经过验证:
//ThreadInterface.h
#pragma once
namespace qing {
class ThreadInterface{
public:
//-----------------------------------------------------------
/*通常情况下,线程的主函数。*/
static void thread_main(cThread *th) {
while (th->chk() != SSHUT) {
while (th->chk() == SSTOP){
th->StopEvent();
}
if (th->chk() == SSTART) {
th->WakeEvent();
}
while (th->chk() == SRUNNING) {
th->LoopEvent();
}
//可能由静止态直接切换到关闭,在清理时需要检查成员变量是否是被设置过的
th->ClearEvent();
}
}//入口
//---------------------------------------------
//程序睡眠阶段的操作函数。
virtual void StopEvent() = 0;
//程序设置阶段的操作函数。
virtual void WakeEvent() = 0;
//程序运行阶段的操作函数。
virtual void LoopEvent() = 0;
//程序清理阶段的操作函数。
virtual void ClearEvent() = 0;
//可以将操作函数从外界传入,
//那么就需要将操作函数设定为静态函数指针
};
}
我们的静态目标函数传入了一个cThread类型参数,这个类型既继承了我们的状态机,又继承了这个线程接口。
-----------------------------------------------------------------------------------------
创建基本线程类
现在,我们可以开始创建我们的cThread类了
//cThread.h
#include "fsm.h"
#include "ThreadInterface.h"
namespace qing{
class cThread: public Fsm, public ThreadInterface {
//一个没有资源的可控线程类型
public:
//构造函数,输入线程的名字
cThread(std::string name);
//暂时删除复制构造函数
cThread(cThread&) = delete;
//不使用虚析构函数将会导致纯虚函数错误
virtual ~cThread();
//对线程名字的设置
void SetLabel(std::string name) {
//对线程名字的设置
this->label = name;
}
//对线程名字的获取
std::string GetLabel() {
//对线程名字的获取
return this->label;
}
//-----------------------------------------------------------
void WaitClose() {
thread->join();
}//等线程关闭
virtual void StopEvent() override{
printer->Print(this->GetLabel(), "注意,原始事件函数被调用。");
Sleep(1)
}
virtual void WakeEvent() override{
printer->Print(this->GetLabel(), "注意,原始事件函数被调用。");
this->run();
}
virtual void LoopEvent() override{
printer->Print(this->GetLabel(), "注意,原始事件函数被调用。");
Sleep(1);
}
virtual void ClearEvent() override {
printer->Print(this->GetLabel(), "注意,原始事件函数被调用。");
}
private:
std::string label;
std::thread *thread = NULL;
};
};
}
// cThread.cpp
#include "cThread.h"
namespace qing {
cThread::cThread(std::string name) {
/*构造函数,完成数据域的设置。*/
this->label = name;
//创建线程的函数
this->thread = new std::thread(thread_main, this);
}
cThread::~cThread(){
if (this->thread) delete this->thread;//删除线程类
} //销毁线程
}
可以看到,这个类除了设置名字,还整合了线程接口以及状态机类。
它的主要功能是创建和销毁持有的线程(其实我觉得可以通过直接继承线程类,还要方便一点)
-----------------------------------------------------------------------------------------
设计我们的线程类
现在我们可以通过继承这个cThread类,覆盖它的事件函数,来执行我们想要的操作了。
对成品类的要求:
1.析构函数中使线程自主关闭。
2.在等待事件中写入Sleep函数。
3.在开始事件中写入状态的改变。
以下是一个实例代码:
//TestThread.h
#pragma once
#include "cThread.h"
namespace qing {
class TestThread: public cThread {
/*测试线程类型*/
public:
/*构造函数,首先调用父类的构造*/
TestThread(std::string name) : CommonThread(name) {}
/*析构函数,关闭线程*/
~TestThread(){
if (this->chk() != SSHUT) this->close();//使线程自主关闭
this->WaitClose();//等待线程退出
}
/*暂时删除复制构造函数*/
TestThread(TestThread&) = delete;
void StopEvent() override {
/*程序睡眠阶段的操作函数,线程等待*/
Sleep(1);
}
void WakeEvent() override {
/*程序唤醒阶段的操作函数,线程获取资源*/
std::cout << "我开始了。\n";
this->run;
}
void LoopEvent() override {
/*程序运转阶段的操作函数,线程执行任务*/
std::cout << "我正在运行。";
}
void LoopEvent() override {
/*程序清理阶段的操作函数,线程释放资源*/
// if(resource) delete resource;
std::cout << "清除。";
}
}
}
接下来我们可以在程序的main函数中创建该线程,并且控制它的状态。
这只是一个简单的实现,我正在加入条件变量等