【QT开发(11)】QT 线程QThread

news2024/11/18 18:30:52

Qt的线程支持与平台无关的:

  • 线程类、
  • 一个线程安全的发送事件方式
  • 跨线程的信号-槽的关联

这使得可以从分利用多处理器机器,有效解决不冻结一份应用程序用户界面的情况下,处理一个耗时操作的问题。

文章目录

  • 1、QThread 一个与平台无关的线程类
      • 队列关联机制
      • 使用terminate() 终止运行时,无法进行一些清理工作
      • 静态函数 currentThreadId() 和 currentThread() 可以返回当前执行的线程的表示符号ID和指针。
      • QThread 提供多个平台无关的睡眠函数,其中 sleep 是秒,msleep是毫秒,usleep是微妙。
      • 例子1 基础 QThread类
      • 例子2 : 使用moveToThread() 函数将普通类改变所在的线程
  • 2 同步线程
      • 例子,信号量 QSemaphore 实现生产者消费者问题。
      • 演示
  • 3 可重入re-entrant和线程安全thread-safe
      • 1、可重入
      • 2、线程安全
  • 4 QObjects
      • 1、QObject的可重入性
      • 2 每个线程的事件循环
      • 3 从其他线程访问QObject 子类
      • 4 跨线程的信号和槽
  • 代码仓库
  • 参考

1、QThread 一个与平台无关的线程类

QThread 从run() 函数开始执行,(一般程序是main 开始执行)。默认是 通过run() 调用exec() 开启事件循环,并在线程内运行一个Qt事件循环。

1、先定义一个 MyThread类线程,继承自QThread
2、MyThread 需要定义run(), run()函数结尾需要有exec() 开启循环。
3、从外部创建该线程的实例,然后调用 start() 来执行这个线程,(start()回去调用run())。
4、每个线程会在开始、结束、终止发送started(),finished(),terminated() 信号
5、可以用isFinished() isRunning() 查询线程状态,可以用wait() 阻塞。
6、堆栈可以从操作系统获得,或者 setStackSize() 设置自定堆栈大小。
7、exec()是启动,调用exit() quit()可停止事件循环。

在QT中,当你多次调用一个线程的start()函数时,通常会发生以下几种情况:
- 如果线程已经在运行,那么再次调用start()函数将不会有任何效果。线程不会重新启动,而且程序也不会报错。这是因为一旦线程开始运行,它就会独立于主线程执行,并且无法通过再次调用start()函数来重新启动。
- 如果线程当前处于未运行状态,并且你多次调用start()函数,那么线程会开始多次运行。也就是说,线程会启动多次,每次调用start()函数都会创建一个新的线程实例。
需要注意的是,如果你想要多次执行相同的线程,那么每次调用start()函数时,你需要确保线程在执行完毕后自行销毁。否则,随着时间的推移,你的应用程序可能会创建越来越多的线程实例,这可能会导致系统资源耗尽,甚至导致程序崩溃。
此外,如果你想要控制线程的运行次数,你可以使用循环或其他控制结构来实现。例如,你可以使用一个计数器来跟踪线程的启动次数,并在达到特定次数后停止启动新的线程实例。

队列关联机制

线程中拥有一个事件循环,使它能够关联其他的线程中的信号到本线程的槽上,基于队列关联机制。

在QT框架中,信号(signal)和槽(slot)是一种重要的机制,它们使得观察者模式在C++中得以实现。

信号(signal):当某个事件发生时,比如按钮被点击,事件源(即按钮)就会发出一个信号。这个发出是没有目的的,类似广播。

槽(slot):如果某个对象对某个信号感兴趣,它就会使用连接(connect)函数,意思是,用它的一个函数(称为槽)来处理这个信号。当信号发出时,被连接的槽函数会自动被回调。

这种机制使得我们能够设计出解耦的程序,增强我们的技术设计能力。例如,一个窗口可以同时与多个按钮连接,当这些按钮被点击时,都会触发相同的窗口更新操作。

  • connect() 函数进行sign和slot 关联,会促使Qt::ConnectionType 的参数设置位Qt::QueuedConnection。
  • 可以使用事件循环的类,例如QTimer和QTcpSocket
  • 线程中无法使用任何界面部件的类。

使用terminate() 终止运行时,无法进行一些清理工作

该函数是很危险的,一般不建议使用。

静态函数 currentThreadId() 和 currentThread() 可以返回当前执行的线程的表示符号ID和指针。

QThread 提供多个平台无关的睡眠函数,其中 sleep 是秒,msleep是毫秒,usleep是微妙。

例子1 基础 QThread类

目的:我们在 窗口中 通过点击SatrtThread 来开启一条线程,在控制台打印信息;通过点击CLoseThread 来关闭这个线程。

1、在窗口新建两个按钮 SatrtThread 和 CLoseThread ;
2、添加一个 线程类

// .h
#include <QThread>

...
class MyThread : public QThread{
    Q_OBJECT

public:
    explicit MyThread(QObject *parent=0);
    void stop();

protected:
    void run();

private:
    volatile bool stopped;
    // volatile 线程的可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。

};
// .cpp

// 构造函数,初始化时,对stopped 赋值。
MyThread::MyThread(QObject *parent) : QThread(parent) {
    stopped = false;
}

// 线程的run()  函数,该线程将就是用来打印数据。
// run()函数结尾exec() 开启循环,我们用while的话,就不需要exec()了
void MyThread::run(){
qreal i =0;
while(!stopped){
    qDebug() << QString("in mythread: %1").arg(i);
    msleep(1000);
    i++;
}
stopped = false;

}

// 结束
void MyThread::stop(){
qDebug() << QString("stopped in mythread");
stopped =true;

}


3、在界面的类中增加MyThread thread;
3.1、然后我们在 按钮的slot 里面用 start() 去开启。我们第一节说过从外部创建该线程的实例,然后调用 start() 来执行这个线程,(start()回去调用run())


void c::on_StartThread_clicked()
{
    thread.start();
}

// 可以用isFinished() isRunning() 查询线程状态,可以用wait() 阻塞。
void c::on_CLoseThread_clicked()
{
    qDebug() << QString("stopped in mythread");
    if(thread.isRunning()){
         thread.stop();
    }
}

4、效果
请添加图片描述

例子2 : 使用moveToThread() 函数将普通类改变所在的线程

在这里插入图片描述

// .h
#include <QThread>

...
class Worker: public QObject{
    Q_OBJECT
public slots:
    void run();
};

class Controller: public QObject{
    Q_OBJECT
    QThread workThread;
public:
Controller();
~Controller();
};
// .cpp
void Worker::run(){
qreal i =0;
while(!stopped){
    qDebug() << QString("in mythread: %1").arg(i);
    msleep(1000);
    i++;
}
}

Controller::Controller(){
Woker *worker = new Worker;
worker->moveToThread(&workThread);
workThread.start();

}
Controller::~Controller(){
workThread.quit();
workThread.exit();
}

另外,可以将线程中的任意对象的任意信号关联到 Worker 的 slots 中,不同信号之间的信号和槽的关联是安全的。关于槽和信号的补充信息可以看【QT开发(12)】QT信号与槽的基本知识点
在这里插入图片描述

2 同步线程

有一种场景,线程之间需要停下来等待其他的线程,例如,两个线程尝试同事访问相同的全局变量,因此需要线程的同步。

1、QMutex 互斥锁,在任何事件至多有一个线程可以获得mutex。如果一个线程尝试获得mutex,而此时mutex 已经被锁住了,则这个线程将会睡眠,直到mutex被解锁为止。互斥锁经常用于对共享数据的访问进行保护(例如,两个线程同事访问数据)

2、QReadWriteLock 读写锁,分为读取访问和写入访问,允许多个线程对数据进行读取。某些情况。替换mutex可以提升多线程并发度。

3、QSemaphore 信号量,是QMutex的一般化,它用来保护一定数量的相同资源,而互斥锁 mutex只能保护一个资源。用信号量QSemaphore 实现生产者和消费者例子。

4、QWaitCondition,条件变量。允许一个线程在一些条件满足的时候唤醒其他的线程。一个或者多个线程可以被阻塞来顶戴一个QWaitCondition,从而设置一个可以用于wakeOne() 和 wakeAll() 的条件。

例子,信号量 QSemaphore 实现生产者消费者问题。

用信号量来保护对生产者线程和消费者线程共享的环形缓冲区的访问。

1、生产者向缓冲区写入数据,指导它达到缓冲区的终点,这时他会从起点开始,重新覆盖已经存在的数据。
2、消费者线程读取产生的数据,将其输出。

这个例子包含了两个类,Producer 和 Consumer,他们继承自 QThread。环形缓冲区用来对这两个类之间的通信,保护缓冲区的信号量被设置位全局变量。

1、建立 B.h 头文件。
我们一般不要再.h 头文件申明全局变量哟,在 .cpp 里面申明全局变量!!

我们定义了两个线程Producer 类和Consumer 类

/*
 * =========================== B.h ==========================
 *                                        CREATE --
 *                                        MODIFY -- 
 * ----------------------------------------------------------
 */
#ifndef CLASS_B_H
#define CLASS_B_H

#include "innDebug.h"
#include <QtCore>
#include <stdio.h>
#include <stdlib.h>
#include <QDebug>

class Producer:public QThread{
public:
    void run();
};

class Consumer:public QThread{
public:
    void run();
};
#endif

2、B.cpp 里面实现 环形缓冲区,以及两个类的run()方法

定义了全局变量,因为不需要在该cpp源文件以外使用这些全局变量。因此,你不需要:1、不需要再.h文件 中申明extern了;2、建议static 修饰全局量,当然也可以不修饰也不会报错,static修饰全局量后就仅仅只允许被本cpp文件使用。

/*
 * =========================== B.cpp ==========================
 *                                        CREATE --
 *                                        MODIFY -- 
 * ----------------------------------------------------------
 */
#include "B.h"
const int DataSize=10;
const int BufferSize=5;
// Producer create circle data buffer!
static char buffer[BufferSize];
QSemaphore freeBytes(BufferSize);
// when init, usedBytes is zero.
QSemaphore usedBytes(BufferSize);


void Producer::run(){
qsrand((QTime(0,0,0).secsTo(QTime::currentTime())));

for (int i =0;i <DataSize;++i)
{
 freeBytes.acquire();
 buffer[i%BufferSize]="ACGT"[(int)qrand()%4];
 qDebug()<< QString("producer:%1").arg(buffer[i%BufferSize]);
 usedBytes.release();
 msleep(300);
}
}

void Consumer::run(){
    for (int i=0;i<DataSize;++i ){
        usedBytes.acquire();
        qDebug() << QString("COnsumer:%1").arg(buffer[i%BufferSize]);
        freeBytes.release();
 msleep(1100);
    }
}


相关解释《Creator快速入门_第三版__霍亚飞编著》

在这里插入图片描述

3、在 主线程类 中定义这两个类

class c : public QDialog
{
    Q_OBJECT

public:
    explicit c(QWidget *parent = nullptr);
    ~c();
    Producer producer;
    Consumer consumer;

4、在主线程的按钮槽里面定义启动这两个类。
如果使用了 wait() 函数,会阻塞主线程直到这两个类完成他们的工作。

void c::on_StartThread_clicked()
{
    producer.start();
    consumer.start();
    //producer.wait();
    //consumer.wait();
}

演示

在这里插入图片描述

3 可重入re-entrant和线程安全thread-safe

用于标记类和函数

  • 线程安全:一个线程安全的函数可以同事被多个线程调用,即使他们共享了数据。因为共享数据的所有实例都被序列化了
  • 一个可重入函数可以同时被多个线程调用,但是只能是在每个调用使用自己的数据的情况下。

一个线程安全的总是可重入的。

注意:只有文档中标记位线程安全的QT类,才可以用于多线程!!!!!
没有标记为线程安全或者可重入,不应该被多线程使用;其类的一个特定实例不应该被多个线程访问。

1、可重入

一般来说,C++类是可重入的,因为他们只访问自己的数据成员。

在这里插入图片描述

可以从截图看出,++ 操作不总是原子操作,不是线程安全的。

2、线程安全

++ 操作需要在完成前,不被其他人中断。一个简单方法就是使用QMutex 对数据成员进行保护。
在这里插入图片描述

在C++中,mutable是一个类型修饰符,用于指定一个成员函数可以修改一个常量对象的成员。具体来说,mutable限定符允许在常量对象上调用该成员函数,并且在该成员函数内部修改该对象的常量成员。
通常,在C++中,如果一个对象被声明为const,那么该对象的成员函数就不能修改该对象的任何成员。这是为了确保对象的不可变性。然而,有时需要在某些特殊情况下修改对象的常量成员,这时就可以使用mutable限定符。
例如,考虑一个只读属性,它通常会被声明为const成员函数,以防止外部代码修改该属性。然而,如果该属性需要在内部被修改,并且只有在特定条件下才需要修改,那么就可以将该属性声明为mutable,并在相应的成员函数中使用该属性。
需要注意的是,使用mutable修饰符可能会破坏对象的封装性,并降低代码的可维护性。因此,应该谨慎使用mutable修饰符,并仅在确实需要的情况下使用。

4 QObjects

QThread 继承自QObject。QObject 可以发射信号并调用其他线程的槽,而且向其他线程中的对象发送事件。这些实现的基础是每个线程都允许有自己的事件循环。

1、QObject的可重入性

大多数非GUI子类可以重入。QTime,QTcpSocket,QUdpSocket,QProcess。
这些类被设计在位单一线程中创建和使用,在一个线程中创建一个对象。然后再另外一个线程中调用这个对象的一个函数是无法保证一定可以工作的!!!

需要注意的3 个约束条件

  • QObject 的子对象必须在创建QObject 的父对象的线程中创建。这就是说,永远不要 将 QThread 对象 作为在该线程中创建对象的父对象,因为QThread 对象本身是在其他线程中创建的。

在Qt中,QObject是所有用户对象的基类。我们可以创建QObject的子对象来创建自定义的QObject子类。下面是一个简单的例子:


#include <QObject>  
  
// 自定义的QObject子类  
class MyObject : public QObject  
{  
    Q_OBJECT  
  
public:  
    MyObject(QObject *parent = nullptr) : QObject(parent) {  
        // 在这里可以添加一些初始化代码  
    }  
  
    // 可以添加一些自定义的信号、槽或者其他成员变量  
};  
  
int main(int argc, char *argv[]) {  
    QCoreApplication app(argc, argv);  
  
    // 创建MyObject的实例  
    MyObject *myObject = new MyObject(&app);  
  
    return app.exec();  

}

这个例子中,我们创建了一个名为MyObject的自定义QObject子类。在MyObject的构造函数中,我们调用了QObject的构造函数,并传递了父对象(在这个例子中是app)。然后,我们可以在MyObject类中添加自定义的信号、槽或者其他成员变量。最后,在main函数中,我们创建了一个MyObject的实例。

main 函数是父进程可以创建QObject 子对象。

  • 事件驱动对象只能在单一的线程中使用。例如,你不能在创建定时器的线程以外的其他线程中启动这个定时器

事件驱动对象是指通过事件来驱动对象行为的计算机程序对象。在事件驱动的系统中,当一个事件发生时,系统会根据事件的类型和状态,调用相应的处理程序或执行相应的操作。事件驱动对象通常包括事件处理器、事件对象和事件队列等组成部分。事件处理器是用于处理事件的对象,它包含了处理事件的程序代码和处理事件所需的数据。事件对象是表示事件的对象,它包含了事件的类型、状态和其他相关信息。事件队列是用于存储事件的对象集合,它按照事件发生的时间顺序排列。在事件驱动的程序中,当一个事件发生时,系统会将该事件对象放入事件队列中。事件处理器会不断地检查事件队列,直到发现一个需要处理的事件。然后,事件处理器会从事件队列中取出该事件对象,并调用相应的处理程序或执行相应的操作来处理该事件。

不可以在对象所在的线程 QObject::thread() 以外的其他线程中,启动一个定时器或者套接字。

在C++中,使用Qt库时,一个QObject对象(及其子对象)的生命周期与创建该对象的线程相同。换句话说,一个QObject对象只能在其所在线程中活动。因此,如果你在一个线程中创建了一个QObject对象(例如,通过调用new QObject()),那么你只能在创建该对象的同一线程中使用该对象。

在多线程编程中,一个常见的问题是跨线程通信。例如,你可能在一个线程中创建了一个定时器(通过new QTimer()),并希望在其他线程中启动这个定时器。然而,由于QObject对象的生命周期与创建它的线程相同,你不能在创建定时器的线程以外的其他线程中启动这个定时器。如果你尝试这样做,可能会导致程序崩溃或其他未定义的行为。

以下是一个例子,展示了为什么不能在对象所在的线程QObject::thread()以外的其他线程中启动一个定时器:

#include <QCoreApplication>
#include <QTimer>
#include <QDebug>

void startTimerInOtherThread() {
    QTimer *timer = new QTimer();
    timer->moveToThread(QThread::currentThread()); // 试图将定时器移动到当前线程
    QObject::connect(timer, &QTimer::timeout, QCoreApplication::instance(), &QCoreApplication::quit);
    timer->start(1000); // 试图在错误的主线程之外启动定时器
}

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);
    QThread thread;
    thread.start();
    startTimerInOtherThread(); // 在新线程中尝试启动定时器
    thread.wait();
    return app.exec();
}

这段代码尝试在一个新线程中创建一个定时器,并在该线程中启动定时器。然而,这会导致程序崩溃,因为定时器试图在其不是主线程的线程中启动。如果你希望在另一个线程中使用定时器,你应该在那个线程中创建定时器。

  • 必须确保删除QThread 对象以前,删除在该线程中创建的所有对象,可以在run函数中的栈上创建对象来保证这一点。

一般,GUI类无法重入,无法在主线程以外的线程中使用GUI类。
在这里插入图片描述

2 每个线程的事件循环

每一个线程都可以有它自己的事件循环。
在这里插入图片描述

在第一节QThread中, MyThread 需要定义run(), run()函数结尾需要有exec() 开启循环。从外部创建该线程的实例,然后调用 start() 来执行这个线程,(start()回去调用run())。exec()是启动,调用exit() quit()可停止事件循环。
在这里插入图片描述

发往这个QObject的事件,有该线程的事件循环进行分派。
在这里插入图片描述
没有允许exec事件循环时,事件不会传递到对象。

可以手动使用线程安全函数 QCoreApplication::postEvent() 在任何事件向任何对象发送事件。该事件会被该对象的线程的事件循环进行分派,如果没有启动exec,应该也不会生效吧。
在这里插入图片描述

QCoreApplication::postEvent()QCoreApplication::sendEvent() 是 Qt 框架中用于处理事件的两个重要方法。它们通常用于处理 GUI 事件,例如按钮点击,窗口关闭等等。这些事件由 Qt 的事件循环系统处理。

  • QCoreApplication::postEvent():此方法将一个事件添加到事件队列中,然后立即返回。事件循环系统会在适当的时候处理这个事件。这样,事件可以在不同的线程中处理,这对于多线程应用程序来说是非常有用的。
  • QCoreApplication::sendEvent():此方法直接将一个事件发送给指定的对象。它不会将事件添加到事件队列中,而是立即处理事件。如果事件的目标对象没有足够的事件处理能力,那么此方法会调用该对象的 reject() 方法。

总的来说,QCoreApplication::postEvent() 更适用于将事件放到事件队列中以便稍后处理,而 QCoreApplication::sendEvent() 更适用于立即处理事件。在选择使用哪一个方法时,需要考虑你的应用程序的特定需求和上下文。

3 从其他线程访问QObject 子类

QObject 子类不是线程安全的。
建议用mutex 保护这个QObject 子类的内部数据的所有访问。

在C++中,你可以使用互斥锁(mutex)来保护QObject子类的内部数据。以下是一个简单的示例代码:

#include <QMutex>
#include <QObject>

class MyQObject : public QObject {
    Q_OBJECT

public:
    MyQObject(QObject *parent = nullptr) : QObject(parent) {
        // 创建一个互斥锁对象
        QMutex mutex;

        // 将互斥锁对象作为保护成员变量
        this->mutex = &mutex;
    }

    void setValue(int value) {
        // 锁定互斥锁
        this->mutex->lock();

        // 在这里执行对内部数据的访问和修改操作
        // ...

        // 解锁互斥锁
        this->mutex->unlock();
    }

    int getValue() const {
        // 锁定互斥锁
        this->mutex->lock();

        // 在这里执行对内部数据的访问操作
        // ...

        // 解锁互斥锁
        this->mutex->unlock();

        return 0; // 返回值可以根据你的需求进行修改
    }

private:
    QMutex *mutex; // 互斥锁对象指针,用于保护内部数据访问
};

在这个示例中,我们创建了一个名为MyQObject的类,它继承自QObject。在MyQObject的构造函数中,我们创建了一个QMutex对象,并将其赋值给一个保护成员变量mutexsetValue()getValue()方法在访问和修改内部数据之前都会锁定互斥锁,然后在完成后解锁。这样可以确保在任何时候只有一个线程能够访问内部数据,从而保护数据的完整性。

QThread的槽一般是不安全的,除非用mutex保护成员变量。但是可以安全的在Qthread:run() 发送信号,因为发射信号是线程安全的。

4 跨线程的信号和槽

在这里插入图片描述

代码仓库

https://gitee.com/hiyanyx/qt5.14-cpp_dds_-project/tree/QThread
分支:QThread

(正文完)

参考

《QT Creator快速入门_第三版__霍亚飞编著》

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

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

相关文章

JAVAEE初阶相关内容第十六弹--网络编程

写在前 这一节的内容首先是对十五弹&#xff08;UDP回显服务器&#xff09;进行简单的改进&#xff0c;在这基础上开始介绍TCP流套接字编程。 目录 写在前 1.改进回显服务器 1.1完整代码实现 1.2运行输出结果 2.TCP流套接字编程 2.1ServerSocketAPI 2.2SocketAPI 3.TC…

JZ23链表中环的入口结点

JZ23链表中环的入口结点 思路: 采用双指针&#xff0c;设定快指针fast_p是慢指针slow_p的2倍&#xff0c;如果有环&#xff0c;则当两指针第一次相遇时慢指针一定不可能在环中走超过一圈&#xff0c;因此假设头结点到环的开头距离为a,环开头到第一次相遇节点的距离为b&#xf…

【数据分享】2023年我国科技型中小企业数据(免费获取/Excel格式/Shp格式)

企业是经济活动的参与主体&#xff0c;一个城市的企业数量决定了这个城市的经济发展水平&#xff01;之前我们分享过2023年高新技术企业数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff0c;我国专精特新“小巨人”企业数据&#xff08;可查看之前的文章获悉详情…

基于深度学习网络的手势识别算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 clc; clear; close all; warning off; addpath(genpath(pwd)); rng(default)load gnet.mat[Pr…

32二叉树——DFS深度优先遍历

目录 深度优先算法&#xff08;Depth-First Search&#xff0c;DFS&#xff09; LeetCode之路——102. 二叉树的层序遍历 分析 深度优先算法&#xff08;Depth-First Search&#xff0c;DFS&#xff09; DFS是一种用于遍历或搜索树状数据结构的算法&#xff0c;其中它首先探…

解决“您点击的链接已过期”;The Link You Followed Has Expired的问题

今天WP碰到一个坑。无论发布文章还是更新插件、更换主题都是这么一种状态“您点击的链接已过期”&#xff1b;The Link You Followed Has Expired 百度出来的答案都是修改post_max_size 方法1. 通过functions.php文件修复 这种方法更容易&#xff0c;只需将以下代码添加到Wor…

程序可以创建多少个用户界面对象?

有人提到这样一个问题&#xff1a;”一个程序最多可以注册多少个窗口类?” 问题的答案不是一个具体的数字。因为大多数用户界面对象都来自一个共享的内存池&#xff0c;我们称之为”桌面堆内存”。尽管我们可以计算一个最大的理论值&#xff0c;但是在实际的场景中&#xff0…

模仿企业微信界面

备注&#xff1a;未实现相关功能&#xff0c;仅模仿界面&#xff0c;不能作为商业用途&#xff0c;若有侵权&#xff0c;请联系删除。 <Window x:Class"模仿企业微信界面.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"…

身为网络工程师必考证书:华为HCIP认证!

为了找到一份心仪的工作&#xff0c;有的人选择入职前先自我提升考取贴合岗位需求的从业相关证书&#xff0c;进而面试并开始工作&#xff0c;有的人选择先从“基层”开始积累经验为首&#xff0c;先进入行业内夯实基础&#xff0c;学习和考证作为了工作“平稳”后的计划。 很…

Qt Creater 设计的登录注册界面 使用SQLite数据库

Qt Creater 设计的登录注册界面 使用SQLite数据库 案例截图 登录页面 注册页面 项目目录结构截图 代码 main.cpp #include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MainWindow w;//第一个是…

AUTOSAR通信篇 - CAN网络通信(七:Nm)

文章目录 基础功能NM协调器功能NM协调器功能的适用性保持协调总线活动总线关闭的协调嵌套子总线的协调关闭定时器的计算同步用例1 – 同步指令同步用例2-同步启动同步用例3 -同步网络睡眠示例 唤醒和中止协调关闭外部的网络唤醒协调唤醒协调关闭的中止 部分网络功能PNC位向量过…

高速电路设计----第三章(2)LVDS信号详解

一、TTL和CMOS不适用于高速电路设计的原因&#xff08;都是数字电路信号&#xff09; 原因&#xff1a; ①电平幅度较大&#xff0c;电平最低都达到了2.5V或者3.3V。因此信号沿变化所需要的时间很长。不适合大于200MHZ的信号。 ②容易被干扰&#xff0c;输出信号为单端&#xf…

Ubuntu OpenLDAP配置笔记

Ubuntu OpenLDAP配置笔记 问题&#xff08;需求&#xff09;LDAP服务端安装slapd和ldap-utils配置域名编辑hosts修改主机名验证增加一个域账号修改用户的密码 Linux桌面加域安装软件验证允许远程账号首次登录时自动创建HOME目录桌面登录 其它问题Ubuntu更新和安装太慢LDAP服务端…

安装了WinRAR,但是右键发现没有压缩选项,怎么办

我们安装了WinRAR之后想要压缩文件&#xff0c;但是右键点击文件之后发现并没有WinRAR压缩选项&#xff0c;这应该如何设置才能出现右键带有压缩选项呢&#xff1f;方法如下&#xff1a; 首先打开WinRAR&#xff0c;在上面功能中点击选项 – 设置 然后我们在设置界面中切换到集…

基于Restful的WebService

目录 Restful简介 1. 资源(Resources) 2. 表述性状态转移(Representational State Transfer) 3. URL(统一资源定位符) 4. 数据格式(Data Format) 5. 状态码(Status Codes) 6. 超媒体(Hypermedia) 7. 无状态性(Statelessness) 8. 资源关系(Resource Relationships) 9.…

WinCC趋势跨度设置(时间范围)

控件&#xff1a;输入输出域、组合框、按钮、实时趋势控件 输入输出域 对象名称&#xff1a;IOI 域类型&#xff1a;输入 组合框 对象名称&#xff1a;cb 索引与文本一一对应 按钮VB Sub OnClick(Byval Item) …

RustCC分享会|非凸科技与开发者共同探讨Rust安全进化

10月15日&#xff0c;非凸科技受邀参加RustCC联合多家开发者社区组织的Global Tour of Rust技术分享活动&#xff0c;旨在为Rust开发者提供交流互动的平台&#xff0c;分享Rust语言的知识、经验和最佳实践。 活动上&#xff0c;非凸科技成都分公司研发总监赵海峰以“Rust安全进…

【框架源码篇 05】Spring源码篇-ApplicationContext

Spring源码篇-ApplicationContext 前面通过手写IoC&#xff0c;DI、AOP和Bean的配置。到最后ApplicationContext的门面处理&#xff0c;对于Spring相关的核心概念应该会比较清楚了。接下来我们就看看在Spring源码中&#xff0c;对于的核心组件是如何实现的。 一、ApplicationC…

光环云入选“北京市算力互联互通试点参与企业”!

为进一步贯彻落实工业和信息化部等六部委联合印发的《算力基础设施高质量发展行动计划》&#xff0c;扩大北京市算力互联互通试点参与范围&#xff0c;助力建设全球数字经济标杆城市&#xff0c;北京市通信管理局组织相关专家对申报第二批参与试点企业开展评估&#xff0c;光环…

docker 部署服务案例

mysql Centos7为例 NAME"CentOS Linux" VERSION"7 (Core)" ID"centos" ID_LIKE"rhel fedora" VERSION_ID"7" PRETTY_NAME"CentOS Linux 7 (Core)" ANSI_COLOR"0;31" CPE_NAME"cpe:/o:centos:cento…