首页 > 编程语言 >C++可控制线程

C++可控制线程

时间:2024-08-18 14:53:29浏览次数:13  
标签:std 函数 cThread void 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 "CommonThread.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函数中创建该线程,并且控制它的状态。

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

标签:std,函数,cThread,void,C++,状态机,线程,控制线
From: https://blog.csdn.net/gapcuda/article/details/141298607

相关文章

  • 鼠标键盘控制c++
     感觉鼠标控制挺好玩的 要想完成鼠标的一系列控制,首先你需要一个头文件:#include<windows.h> 以下是鼠标单击左键的代码,可以做成子程序(我是背下来的):mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//按下左键Sleep(10);//要给一些应用反应时间mouse_event(MOUSEEVENTF_L......
  • 【全网独家】OpenCV C++ 图像处理实战 :多二维码识别(代码+测试部署)
    介绍在现代社会,二维码无处不在,从支付、物流到用户身份验证,二维码的应用极其广泛。本文将详细介绍如何使用OpenCV在C++环境下实现多二维码识别。我们将涵盖其应用场景、原理解释、算法流程图以及实际代码实现。应用使用场景仓储物流管理:快速扫描多个包裹上的二维码,实现高......
  • c++ builder哪个版本更好用
    1、当前,功能相对完全和成熟的是XE7。2、如果开发传统的程序,C++BUILDER2006最成熟轻量。二、可以难说哪个更好用,每个版本都有它自个的特点,典型的版的本个人理解供你参考:1、C++BUILDER4.0是BCB(C++BUILDER的简称)的第一个win下的版本,后继还有个小升级到C++BUILDER4.5,如果你想在......
  • C++入门篇一
    C++入门篇一一.缺省参数1.缺省参数的概念2.缺省参数分类二.函数重载1.函数重载概念2.函数重载代码举例三.引用1.引用的概念2.引用特性3.常引用4.使用场景(1).做参数(2).做返回值5.传值、传引用效率比较6.引用和指针的区别7.引用和指针的不同点一.缺省......
  • [C++ Error] f0201.cpp(11): E2379 Statement missing ;
    错误解释:这个错误表明在C++源代码文件f0201.cpp的第11行出现了一个语法错误,具体是缺少了一个分号;。C++语言规定语句的结束需要使用分号;,如果一个语句缺少了它,编译器就会抛出这样的错误。解决方法:打开f0201.cpp文件``,定位到第11行。检查那一行的代码,确保每个语句后面都有分号;......
  • JetBrains CLion 2024.2 (macOS, Linux, Windows) - C 和 C++ 跨平台 IDE
    JetBrainsCLion2024.2(macOS,Linux,Windows)-C和C++跨平台IDEJetBrains跨平台开发者工具请访问原文链接:https://sysin.org/blog/jetbrains-clion/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgJetBrainsCLion-C和C++跨平台IDE用于强大语言的......
  • C++做算法题,容器知识看这一篇就够啦!
    C++常用容器分享(算法题,掌握这些就够了)vector是什么连续的顺序存储结构,其实就是一个可变数组想使用的话记得#include<vector>怎么用初始化语法vector<类型>名字(长度,初始值)演示一下//一维的vectorvector<int>arr;//不指定里面有多少个元素vector<int>a......
  • 【C++小白到大牛】红黑树那些事儿
    目录前言:一、红黑树的概念二、红黑树的性质三、红黑树结点的定义四、红黑树的插入情况一:u存在且为红情况二:u不存在/u存在且为黑小总结:原码:五、红黑树的检验六、性能比较前言:我们之前已经学过了二叉搜索树的优化版——AVL树,这次我们来学习二叉搜索树的另外一种优......
  • 成绩排序—————c++
    先看问题:成绩排序时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++256MB,其他语言512MB难度:普及-分数:100 OI排行榜得分:12(0.1*分数+2*难度)出题人:root描述给出班里某门课程的成绩单,请你按成绩从高到低对成绩单排序输出,如果有相同分数则名字字典序小的在前。输入......
  • C++输出
    Hello!Hi!这是我的第一个程序如何输出上面的文字?C++提供了一个函数——cout1|#include<iostream>2|3|intmain()4|{5|cout<<"Hello!";6|return0;7|}以上代码是cout的应用话说回来,如何实现文章开头的效果呢?你可能会用以下代码1|cout<<"Hello!";2|cout<......