【开发语言】层次状态机(HSM)介绍

news2024/12/4 16:06:00

层次状态机(Hierarchical State Machine, HSM),从基本原理、结构设计、实现方法以及如何结合 Qt 进行具体实现等方面进行分析。

1. 层次状态机的基本原理

层次状态机是一种用于管理复杂系统行为的状态机模型,它通过将状态组织成层次结构来简化设计和维护。这种结构使得复杂的逻辑可以分解为更小、更易于管理的部分。

关键概念:
  • 状态(State): 系统在某一时刻所处的条件或模式。
  • 事件(Event): 触发状态转换的信息或信号。
  • 转换(Transition): 从一个状态到另一个状态的迁移过程。
  • 动作(Action): 在进入、离开状态或进行转换时执行的操作。
  • 父状态和子状态:
    • 父状态(Superstate): 包含多个子状态的状态。
    • 子状态(Substate): 属于某个父状态的更具体的状态。
特点:
  • 嵌套结构: 状态可以嵌套在其他状态中,形成层次结构。
  • 继承行为: 子状态可以继承父状态的行为和动作。
  • 事件委托: 事件可以从子状态传递到父状态进行处理。
  • 简化设计: 将复杂的状态逻辑分解为更小的、可管理的部分。

2. 层次状态机的设计

在设计层次状态机时,需要仔细规划状态之间的关系以及如何组织这些状态。以下是一些设计原则和步骤:

设计步骤:
  1. 识别顶级状态:

    • 确定系统的基本操作模式或主要功能。
    • 例如,在一个电梯系统中,顶级状态可能包括“待命”、“运行”和“维护”。
  2. 定义子状态:

    • 对于每个顶级状态,进一步分解为更具体的状态。
    • 例如,“运行”状态可以包含“上升”、“下降”和“停止”等子状态。
  3. 确定事件和转换:

    • 定义可能触发状态转换的事件。
    • 确定每个状态在接收到特定事件时应执行的操作以及如何进行转换。
    • 例如,“上升”状态在接收到“到达楼层”事件时,可以转换到“停止”状态。
  4. 实现继承和委托:

    • 设计父状态的行为,并让子状态继承这些行为。
    • 当子状态无法处理某个事件时,将该事件传递给其父状态进行处理。
  5. 编写动作函数:

    • 实现每个状态的进入(Entry)、离开(Exit)操作以及转换期间的动作(Action)。
    • 例如,在“上升”状态下进入时启动电机,在离开时停止电机。
  6. 定义初始状态和历史状态:

    • 指定每个复合状态的初始子状态。
    • 使用历史状态来记住上次活动的子状态,以便在返回该状态时恢复到之前的状态。
示例:

假设我们设计一个简单的电视遥控器状态机,包含以下状态:

  • 待命(Standby)
  • 运行(Running)
    • 频道选择(Channel Selection)
      • 浏览模式(Browse Mode)
      • 锁定模式(Lock Mode)
    • 音量控制(Volume Control)

事件包括:

  • POWER
  • CHANNEL_UP, CHANNEL_DOWN
  • VOLUME_UP, VOLUME_DOWN
  • MODE_SWITCH
状态图示例:
          +-------------------+
          |     Standby       |
          +--------+----------+
                   |
                 POWER
                   |
              +----v-----+
              |  Running   |
              +--+-+------+
                 | |
           CHANNEL_UP|VOLUME_DOWN
             /      |      \
            /       v       \
    +------------+     +------------+
    | Browse Mode|     |Volume Ctrl|
    +------------+     +------------+

3. 层次状态机的实现

在实际编程中,层次状态机可以通过多种方式实现。以下是一个使用 C++ 和 Qt 的具体示例。

使用结构体定义状态和事件

首先,我们定义状态、事件及其处理函数的类型:

#include <QObject>
#include <QVector>
#include <QDebug>

namespace HSMUtilityDef {
    typedef uint32_t HSM_EVENT;

    // 定义常见事件
    const uint32_t MAX_DEPTH = 5;
    const HSM_EVENT HSME_NULL = 0;
    const HSM_EVENT HSME_START = 1;
    const HSM_EVENT HSME_INIT = static_cast<HSM_EVENT>(-3);
    const HSM_EVENT HSME_ENTRY = static_cast<HSM_EVENT>(-2);
    const HSM_EVENT HSME_EXIT = static_cast<HSM_EVENT>(-1);

    // 自定义事件
    const HSM_EVENT POWER = 100;
    const HSM_EVENT CHANNEL_UP = 101;
    const HSM_EVENT CHANNEL_DOWN = 102;
    const HSM_EVENT VOLUME_UP = 103;
    const HSM_EVENT VOLUME_DOWN = 104;
    const HSM_EVENT MODE_SWITCH = 105;
}
定义状态基类

创建一个抽象基类 HSMState,包含处理事件的虚函数:

class HSMState : public QObject {
    Q_OBJECT

public:
    explicit HSMState(HSMState *parent = nullptr) : QObject(parent), m_parent(parent) {}

    virtual ~HSMState() {}

    // 处理事件的主要接口
    virtual HSMUtilityDef::HSM_EVENT handleEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {
        switch (event) {
            case HSMUtilityDef::HSME_ENTRY:
                onEntry(param);
                break;
            case HSMUtilityDef::HSME_EXIT:
                onExit(param);
                break;
            default:
                return unhandledEvent(event, param);
        }
        return HSMUtilityDef::HSME_NULL;
    }

protected:
    // 进入状态时执行的动作
    virtual void onEntry(void* param) {}

    // 离开状态时执行的动作
    virtual void onExit(void* param) {}

    // 处理未定义事件的方法
    virtual HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) {
        qDebug() << "Unhandled event" << event;
        return event;
    }

    // 获取父状态
    HSMState* parent() const { return m_parent; }

private:
    HSMState *m_parent;
};
定义具体状态类

创建具体的派生状态类,实现特定的逻辑:

// 待命状态(Standby)
class StandbyState : public HSMState {
    Q_OBJECT

public:
    explicit StandbyState(HSMState* parent = nullptr) : HSMState(parent) {}

protected:
    void onEntry(void* param) override {
        qDebug() << "Entering Standby State";
    }

    void onExit(void* param) override {
        qDebug() << "Exiting Standby State";
    }

    HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {
        if (event == HSMUtilityDef::POWER) {
            return static_cast<HSMUtilityDef::HSM_EVENT>(HSMUtilityDef::HSME_INIT);
        }
        return HSMState::unhandledEvent(event, param);
    }
};

// 运行状态(Running)
class RunningState : public HSMState {
    Q_OBJECT

public:
    explicit RunningState(HSMState* parent = nullptr) : HSMState(parent) {}

protected:
    void onEntry(void* param) override {
        qDebug() << "Entering Running State";
    }

    void onExit(void* param) override {
        qDebug() << "Exiting Running State";
    }

    HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {
        if (event == HSMUtilityDef::POWER) {
            return static_cast<HSMUtilityDef::HSM_EVENT>(HSMUtilityDef::HSME_EXIT);
        }
        return HSMState::unhandledEvent(event, param);
    }
};

// 频道选择状态(Channel Selection)
class ChannelSelectionState : public HSMState {
    Q_OBJECT

public:
    explicit ChannelSelectionState(HSMState* parent = nullptr) : HSMState(parent), m_currentMode(BROWSE_MODE) {}

protected:
    void onEntry(void* param) override {
        qDebug() << "Entering Channel Selection State";
    }

    void onExit(void* param) override {
        qDebug() << "Exiting Channel Selection State";
    }

    HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {
        switch (event) {
            case HSMUtilityDef::CHANNEL_UP:
                channelUp();
                break;
            case HSMUtilityDef::CHANNEL_DOWN:
                channelDown();
                break;
            case HSMUtilityDef::MODE_SWITCH:
                modeSwitch();
                break;
            default:
                return HSMState::unhandledEvent(event, param);
        }
        return HSMUtilityDef::HSME_NULL;
    }

private:
    enum Mode {
        BROWSE_MODE,
        LOCK_MODE
    };

    Mode m_currentMode;

    void channelUp() {
        if (m_currentMode == BROWSE_MODE) {
            qDebug() << "Channel Up in Browse Mode";
        } else if (m_currentMode == LOCK_MODE) {
            qDebug() << "Channel Up in Lock Mode";
        }
    }

    void channelDown() {
        if (m_currentMode == BROWSE_MODE) {
            qDebug() << "Channel Down in Browse Mode";
        } else if (m_currentMode == LOCK_MODE) {
            qDebug() << "Channel Down in Lock Mode";
        }
    }

    void modeSwitch() {
        if (m_currentMode == BROWSE_MODE) {
            m_currentMode = LOCK_MODE;
            qDebug() << "Switched to Lock Mode";
        } else {
            m_currentMode = BROWSE_MODE;
            qDebug() << "Switched to Browse Mode";
        }
    }
};

// 音量控制状态(Volume Control)
class VolumeControlState : public HSMState {
    Q_OBJECT

public:
    explicit VolumeControlState(HSMState* parent = nullptr) : HSMState(parent) {}

protected:
    void onEntry(void* param) override {
        qDebug() << "Entering Volume Control State";
    }

    void onExit(void* param) override {
        qDebug() << "Exiting Volume Control State";
    }

    HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {
        switch (event) {
            case HSMUtilityDef::VOLUME_UP:
                volumeUp();
                break;
            case HSMUtilityDef::VOLUME_DOWN:
                volumeDown();
                break;
            default:
                return HSMState::unhandledEvent(event, param);
        }
        return HSMUtilityDef::HSME_NULL;
    }

private:
    void volumeUp() {
        qDebug() << "Volume Up";
    }

    void volumeDown() {
        qDebug() << "Volume Down";
    }
};
定义层次状态机类

创建一个管理状态转换的主类 HSM

class HSM : public QObject {
    Q_OBJECT

public:
    explicit HSM(QObject* parent = nullptr) : QObject(parent), m_currentState(nullptr) {}

    void start(HSMState* initialState) {
        if (m_currentState == nullptr) {
            initialize(initialState);
        }
    }

    void processEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {
        if (m_currentState != nullptr) {
            HSMState* nextState = m_currentState;
            HSMUtilityDef::HSM_EVENT nextEvent = event;

            // 处理事件,直到返回 HSME_NULL
            while (nextEvent != HSMUtilityDef::HSME_NULL) {
                nextState = processSingleEvent(nextState, nextEvent, param);
                nextEvent = nextState->handleEvent(event, param);
            }
        }
    }

private:
    HSMState* m_currentState;

    void initialize(HSMState* initialState) {
        if (initialState != nullptr) {
            // 初始化状态栈
            QVector<HSMState*> stateStack;
            while (initialState != nullptr) {
                stateStack.append(initialState);
                initialState = initialState->parent();
            }

            // 从顶层状态开始初始化
            for (int i = stateStack.size() - 1; i >= 0; --i) {
                HSMState* currentState = stateStack[i];
                currentState->handleEvent(HSMUtilityDef::HSME_ENTRY, nullptr);
            }

            m_currentState = stateStack.last();
        }
    }

    HSMState* processSingleEvent(HSMState* currentState, HSMUtilityDef::HSM_EVENT event, void* param) {
        switch (event) {
            case HSMUtilityDef::HSME_INIT:
                return initializeChildStates(currentState);
            case HSMUtilityDef::HSME_ENTRY:
                currentState->onEntry(param);
                break;
            case HSMUtilityDef::HSME_EXIT:
                currentState->onExit(param);
                return processSingleEvent(currentState->parent(), HSMUtilityDef::HSME_EXIT, param);
        }
        return currentState;
    }

    HSMState* initializeChildStates(HSMState* parentState) {
        if (parentState == nullptr) {
            return nullptr;
        }

        QVector<HSMState*> childStates = findInitialStates(parentState);

        for (HSMState* state : childStates) {
            processSingleEvent(state, HSMUtilityDef::HSME_ENTRY, nullptr);
        }

        return childStates.last();
    }

    QVector<HSMState*> findInitialStates(HSMState* parentState) const {
        // 在实际应用中,可能需要更复杂的逻辑来确定初始子状态
        // 这里简单地假设每个父状态只有一个直接的初始子状态

        QVector<HSMState*> children;
        QObjectList childObjects = parentState->children();
        for (QObject* obj : childObjects) {
            HSMState* state = qobject_cast<HSMState*>(obj);
            if (state != nullptr) {
                children.append(state);
            }
        }

        // 返回第一个子状态作为初始状态
        return children;
    }
};
构建和运行状态机

main 函数中构建并运行层次状态机:

#include <QCoreApplication>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    // 创建状态对象
    StandbyState* standby = new StandbyState();
    RunningState* running = new RunningState(standby);
    ChannelSelectionState* channelSel = new ChannelSelectionState(running);
    VolumeControlState* volumeCtrl = new VolumeControlState(running);

    // 构建层次结构
    running->setParent(standby);
    channelSel->setParent(running);
    volumeCtrl->setParent(running);

    // 创建状态机并启动
    HSM hsm;
    hsm.start(standby);

    // 处理事件
    hsm.processEvent(HSMUtilityDef::POWER);          // 切换到运行模式
    hsm.processEvent(HSMUtilityDef::CHANNEL_UP);       // 选择频道向上
    hsm.processEvent(HSMUtilityDef::MODE_SWITCH);      // 切换到锁定模式
    hsm.processEvent(HSMUtilityDef::VOLUME_UP);        // 增加音量
    hsm.processEvent(HSMUtilityDef::POWER);          // 关闭电视

    return a.exec();
}

4. 使用 Qt 的信号和槽机制增强状态机

Qt 提供了强大的信号和槽机制,可以用来进一步简化状态机的设计和实现。以下是如何将 Qt 的信号和槽与层次状态机结合使用的方法。

修改 HSMState 类以支持信号和槽

HSMState 中添加信号来通知状态转换或动作执行:

#include <QObject>
#include <QVector>
#include <QDebug>

namespace HSMUtilityDef {
    typedef uint32_t HSM_EVENT;

    // 定义常见事件
    const uint32_t MAX_DEPTH = 5;
    const HSM_EVENT HSME_NULL = 0;
    const HSM_EVENT HSME_START = 1;
    const HSM_EVENT HSME_INIT = static_cast<HSM_EVENT>(-3);
    const HSM_EVENT HSME_ENTRY = static_cast<HSM_EVENT>(-2);
    const HSM_EVENT HSME_EXIT = static_cast<HSM_EVENT>(-1);

    // 自定义事件
    const HSM_EVENT POWER = 100;
    const HSM_EVENT CHANNEL_UP = 101;
    const HSM_EVENT CHANNEL_DOWN = 102;
    const HSM_EVENT VOLUME_UP = 103;
    const HSM_EVENT VOLUME_DOWN = 104;
    const HSM_EVENT MODE_SWITCH = 105;
}

class HSMState : public QObject {
    Q_OBJECT

public:
    explicit HSMState(HSMState *parent = nullptr) : QObject(parent), m_parent(parent) {}

    virtual ~HSMState() {}

    // 处理事件的主要接口
    virtual HSMUtilityDef::HSM_EVENT handleEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {
        switch (event) {
            case HSMUtilityDef::HSME_ENTRY:
                onEntry(param);
                break;
            case HSMUtilityDef::HSME_EXIT:
                onExit(param);
                break;
            default:
                return unhandledEvent(event, param);
        }
        return HSMUtilityDef::HSME_NULL;
    }

signals:
    // 信号用于通知状态转换或动作执行
    void stateEntered(HSMState* state);
    void stateExited(HSMState* state);

protected:
    // 进入状态时执行的动作
    virtual void onEntry(void* param) {
        emit stateEntered(this);
    }

    // 离开状态时执行的动作
    virtual void onExit(void* param) {
        emit stateExited(this);
    }

    // 处理未定义事件的方法
    virtual HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) {
        qDebug() << "Unhandled event" << event;
        return event;
    }

    // 获取父状态
    HSMState* parent() const { return m_parent; }

private:
    HSMState *m_parent;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2253131.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

亚马逊云(AWS)使用root用户登录

最近在AWS新开了服务器&#xff08;EC2&#xff09;&#xff0c;用于学习&#xff0c;遇到一个问题就是默认是用ec2-user用户登录&#xff0c;也需要密钥对。 既然是学习用的服务器&#xff0c;还是想直接用root登录&#xff0c;下面开始修改&#xff1a; 操作系统是&#xff1…

Android笔记【12】脚手架Scaffold和导航Navigation

一、前言 学习课程时&#xff0c;对于自己不懂的点的记录。 对于cy老师第二节课总结。 二、内容 1、PPT介绍scaffold 2、开始代码实操 先新建一个screen包&#xff0c;写一个Homescreen函数&#xff0c;包括四个页面。 再新建一个compenent包&#xff0c;写一个displayText…

HookVip4.0.3 | 可解锁各大应用会员

HookVip是一款可以解锁会员的模块工具&#xff0c;需要搭配相应框架结合使用。这款插件工具支持多种框架如LSPosed、LSPatch、太极、应用转生等&#xff0c;并且完全免费&#xff0c;占用内存小。支持的软件包括now要想、神奇脑波、塔罗牌占卜、爱剪辑、人人视频、咪萌桌面宠物…

猎板 PCB特殊工艺:铸就电子行业核心竞争力新高度

在当今竞争激烈且技术驱动的电子制造领域&#xff0c;印制电路板&#xff08;PCB&#xff09;作为电子产品的关键基石&#xff0c;其特殊工艺的发展水平直接影响着整个行业的创新步伐与产品品质。猎板 PCB 凭借在厚铜板、孔口铺铜、HDI 板、大尺寸板以及高频高速板等特殊工艺方…

【教学类-43-25】20241203 数独3宫格的所有可能-使用模版替换(12套样式,空1格-空8格,每套510张,共6120小图)

前期做数独惨宫格的所有排列&#xff0c;共有12套样式&#xff0c;空1格-空8格&#xff0c;每套510张&#xff0c;共6120小图&#xff09; 【教学类-43-24】20241127 数独3宫格的所有可能&#xff08;12套样式&#xff0c;空1格-空8格&#xff0c;每套510张&#xff0c;共6120…

Redis+Caffeine 多级缓存数据一致性解决方案

RedisCaffeine 多级缓存数据一致性解决方案 背景 之前写过一篇文章RedisCaffeine 实现两级缓存实战&#xff0c;文章提到了两级缓存RedisCaffeine可以解决缓存雪等问题也可以提高接口的性能&#xff0c;但是可能会出现缓存一致性问题。如果数据频繁的变更&#xff0c;可能会导…

echarts地图立体效果,echarts地图点击事件,echarts地图自定义自定义tooltip

一.地图立体效果 方法1:两层地图叠加 实现原理:geo数组中放入两个地图对象,通过修改zlevel属性以及top,left,right,bottom形成视觉差 配置项参考如下代码: geo: [{zlevel: 2,top: 96,map: map,itemStyle: {color: #091A51ee,opacity: 1,borderWidth: 2,borderColor: #16BAFA…

D87【python 接口自动化学习】- pytest基础用法

day87 pytest运行参数 -m -k 学习日期&#xff1a;20241203 学习目标&#xff1a;pytest基础用法 -- pytest运行参数-m -k 学习笔记&#xff1a; 常用运行参数 pytest运行参数-m -k pytest -m 执行特定的测试用例&#xff0c;markers最好使用英文 [pytest] testpaths./te…

总结拓展十七:特殊采购业务——委外业务

SAP中委外采购业务&#xff0c;又称供应商分包&#xff08;或外协、转包、、外包、托外等&#xff09;&#xff0c;是企业将部分生产任务委托给外部供应商/集团其他分子公司完成的一种特殊采购业务模式。 委外业务主要有2大类型&#xff0c;分别是标准委外&#xff08;委外采购…

ESP8266作为TCP客户端或者服务器使用

ESP8266模块&#xff0c;STA模式&#xff08;与手机搭建TCP通讯&#xff0c;EPS8266为服务端&#xff09;_esp8266作为station-CSDN博客 ESP8266模块&#xff0c;STA模式&#xff08;与电脑搭建TCP通讯&#xff0c;ESP8266 为客户端&#xff09;_esp8266 sta 连接tcp-CSDN博客…

ATTCK红队评估实战靶场(四)

靶机链接&#xff1a;http://vulnstack.qiyuanxuetang.net/vuln/detail/6/ 环境搭建 新建两张仅主机网卡&#xff0c;一张192.168.183.0网段&#xff08;内网网卡&#xff09;&#xff0c;一张192.168.157.0网段&#xff08;模拟外网网段&#xff09;&#xff0c;然后按照拓补…

C 语言 “神秘魔杖”—— 指针初相识,解锁编程魔法大门(一)

文章目录 一、概念1、取地址操作符&#xff08;&&#xff09;2、解引用操作符&#xff08;*&#xff09;3、指针变量1、 声明和初始化2、 用途 二、内存和地址三、指针变量类型的意义1、 指针变量类型的基本含义2、 举例说明不同类型指针变量的意义 四、const修饰指针1、co…

封装loding加载动画的请求

图片 /*** Loading 状态管理类*/ export class Loading {constructor(timer300) {this.value falsethis.timer timer}/*** 执行异步操作并自动管理 loading 状态* param {Promise|Function|any} target - Promise、函数或其他值* returns {Promise} - 返回请求结果*/async r…

人形机器人训练、机器臂远程操控、VR游戏交互、影视动画制作,一副手套全部解决!

广州虚拟动力基于自研技术推出了多节点mHand Pro动捕数据手套&#xff0c;其最大的特点就是功能集成与高精度捕捉&#xff0c;可以用于人形机器人训练、机器臂远程操控、VR游戏交互、影视动画制作等多种场景。 一、人形机器人训练 mHand Pro动捕数据手套双手共装配16个9轴惯性…

Nginx Web服务器管理、均衡负载、访问控制与跨域问题

Nginx Web 服务器的均衡负载、访问控制与跨域问题 Nginx 的配置 1. 安装Nginx 首先安装Nginx apt install nginx -ycaccpurgatory-v:~$ sudo apt install nginx [sudo] password for cacc: Reading package lists... Done Building dependency tree... Done Reading state i…

Bert+CRF的NER实战

CRF&#xff08;条件随机场-Conditional Random Field&#xff09; 原始本文&#xff1a;我在北京吃炸酱面 标注示例&#xff08;采用BIO标注方式&#xff09;&#xff1a; 我O在O北B-PLA京I-PLA吃O炸B-FOOD酱I-FOOD面I-FOOD CRF&#xff1a; 目的&#xff1a;提出一些不可能…

C++语法·识

人生建议&#xff1a;请手机反省一下&#xff0c;为什么总拉着我熬夜。 目录 STL简介 string类容器一 auto&#xff08;自动声明类型&#xff09; 简介&#xff1a; 特点 范围for&#xff08;语法糖&#xff09; 简介 特点 string string类的常见接口 1.构造 2.容…

蓝桥杯准备训练(lesson1,c++方向)

前言 报名参加了蓝桥杯&#xff08;c&#xff09;方向的宝子们&#xff0c;今天我将与大家一起努力参赛&#xff0c;后序会与大家分享我的学习情况&#xff0c;我将从最基础的内容开始学习&#xff0c;带大家打好基础&#xff0c;在每节课后都会有练习题&#xff0c;刚开始的练…

【开源】A059-基于SpringBoot的社区养老服务系统的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看项目链接获取⬇️&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600个选题ex…

winform跨线程更新界面

前言&#xff1a; 大家好&#xff0c;我是上位机马工&#xff0c;硕士毕业4年年入40万&#xff0c;目前在一家自动化公司担任软件经理&#xff0c;从事C#上位机软件开发8年以上&#xff01;我们在开发C#程序的时候&#xff0c;有时候需要在非Ui主线程更新界面&#xff0c;为了…