C++可控制线程

大家好,本人是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函数中创建该线程,并且控制它的状态。

这只是一个简单的实现,我正在加入条件变量等

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值