当前位置: 首页 > news >正文

Qt多线程与QTimer详解

引用

Qt多线程中使用QTimer(常见问题汇总)

[多线程]多线程使用QTimer

Qt::ConnectionType:Qt不同类型connect的详细区别说明与应用

Qt的4种多线程实现方式

一文搞定之Qt多线程(QThread、moveToThread)

QTimer

The QTimer class provides repetitive and single-shot timers.

The QTimer class provides a high-level programming interface for timers. To use it, create a QTimer, connect its timeout() signal to the appropriate slots, and call start(). From then on, it will emit the timeout() signal at constant intervals.

使用QTimer类定义一个定时器,它可以不停重复,也可以只进行一次便停止

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1000);

创建一个QTimer对象,将信号timeout()与相应的槽函数相连,然后调用start()函数。接下来,每隔一段时间,定时器便会发出一次timeout()信号

Qt多线程的实现

QThread::run()

QThread 是 Qt 中实现多线程的基础类,通过继承 QThread 类并重写其 run() 函数可以实现自定义线程逻辑

  • 线程类
#ifndef WORKER_H
#define WORKER_H#include <QThread>class Worker : public QThread
{
public:Worker();void run();void printFunc();};#endif // WORKER_H
#include "Worker.h"
#include <QDebug>Worker::Worker()
{}void Worker::run()
{qDebug()<<"子线程ThreadID: "<<QThread::currentThreadId();
}void Worker::printFunc()
{qDebug()<<"子线程成员函数ThreadID: "<<QThread::currentThreadId();
}
  • main函数
#include <iostream>
#include <QDebug>
#include "Worker.h"using namespace std;int main()
{Worker w;w.start();qDebug()<<"主线程ThreadID: "<<QThread::currentThreadId();w.printFunc();while (1){}return 0;
}
  • 执行结果
子线程ThreadID: 0x4138
主线程ThreadID: 0x34b0
子线程成员函数ThreadID: 0x34b0

主线程和子线程执行的顺序不确定,偶尔主线程在前,偶尔子线程在前

子线程类的成员函数包括槽函数是运行在主线程当中的,只有run()函数运行在子线程中

如果在run()函数中调用子线程类成员函数,那么该成员函数运行在子线程中

  • 在run()函数中调用子线程成员函数
#include "Worker.h"
#include <QDebug>Worker::Worker()
{}void Worker::run()
{qDebug()<<"子线程ThreadID: "<<QThread::currentThreadId();printFunc();
}void Worker::printFunc()
{qDebug()<<"子线程成员函数ThreadID: "<<QThread::currentThreadId();
//    emit doTask();
}
主线程ThreadID: 0x2140
子线程ThreadID: 0x2044
子线程成员函数ThreadID: 0x2044

QThread::moveToThread()

moveToThread() 是 Qt 中用于将对象移动到另一个线程的方法。通过调用 moveToThread() 函数,可以将一个 QObject 对象从当前线程移动到另一个线程中,从而实现对象在新线程中执行特定的任务

在多线程编程中,通常会使用 moveToThread() 方法来将耗时的任务或需要在单独线程中执行的逻辑移动到单独的线程中,以避免阻塞主线程(通常是 GUI 线程)的执行

  • 线程类
#ifndef WORKER_H
#define WORKER_H#include <QObject>class Worker : public QObject
{Q_OBJECT
public:Worker();void printFunc();public slots:void doWork();void doWork2();void doWork3();signals:void testdoWork3();};#endif // WORKER_H
#include "Worker.h"
#include <QDebug>
#include <QThread>Worker::Worker()
{}void Worker::printFunc()
{qDebug() << "成员函数ThreadID:"<<QThread::currentThreadId();}void Worker::doWork()
{qDebug() << "doWork ThreadID:"<<QThread::currentThreadId();
}void Worker::doWork2()
{qDebug() << "doWork2 ThreadID:"<<QThread::currentThreadId();
}void Worker::doWork3()
{qDebug() << "doWork3 ThreadID:"<<QThread::currentThreadId();
}
  • main函数
#include "mainwindow.h"#include <QApplication>
#include "Worker.h"
#include <QDebug>
#include <QThread>int main(int argc, char *argv[]) {QApplication a(argc, argv);//MainWindow w;//w.show();Worker worker;QThread thread;worker.moveToThread(&thread);QObject::connect(&thread, &QThread::started, &worker, &Worker::doWork);         //第一槽函数QObject::connect(&thread, &QThread::started, &worker, &Worker::doWork2);        //第二槽函数QObject::connect(&worker, &Worker::testdoWork3, &worker, &Worker::doWork3);     //第三槽函数//启动线程thread.start();//调用成员数worker.printFunc();//发送自定义信号emit worker.testdoWork3();while (1) {}return a.exec();
}
  • 执行结果
成员函数ThreadID: 0x1330
doWork ThreadID: 0x4070
doWork2 ThreadID: 0x4070
doWork3 ThreadID: 0x4070

槽函数无论是线程的信号触发还是自定义信号触发,槽函数都在新线程里运行

成员函数和主函数运行在主线程当中

QtConcurrent::run()

QtConcurrent::run 是 Qt 库中的一个用于并行执行任务的函数。它允许你在一个独立的线程中运行一个函数,而无需显式地创建和管理线程。这是通过将函数和其参数传递给 QtConcurrent::run 来实现的,Qt 会自动管理线程池和线程的生命周期

QtConcurrent::run能够方便快捷的将任务丢到子线程中去执行,无需继承任何类,也不需要重写函数,使用非常简单。需要注意的是,由于该线程取自全局线程池QThreadPool,函数不能立马执行,需要等待线程可用时才会运行

使用QtConcurrent::run()的基本步骤:

  • 传递一个函数或可调用对象(例如 lambda、函数指针、成员函数等)给 QtConcurrent::run
  • QtConcurrent::run 会在后台线程中执行该函数,并返回一个 QFuture 对象,表示异步操作的结果
  • 可以通过 QFuture 来监控任务的进度和结果

基本语法

QFuture<TResult> QtConcurrent::run(Func func, Args... args);
  • func:要执行的函数或可调用对象
  • args:传递给函数的参数
#include <QCoreApplication>
#include <QtConcurrent>
#include <QDebug>
#include <QThread>
#include <QFuture>void slowFunction(int seconds) {qDebug() << "Start slow function in thread:" << QThread::currentThreadId();QThread::sleep(seconds);  // 模拟耗时操作qDebug() << "Finished slow function";
}int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 在后台线程执行 slowFunctionQFuture<void> future = QtConcurrent::run(slowFunction, 5); // 传入参数 5 表示等待 5 秒// 在此可以执行其他操作,不会被阻塞qDebug() << "Main thread doing something else...";// 等待后台任务完成future.waitForFinished();return a.exec();
}
  • QtConcurrent::run(slowFunction, 5) 会在后台线程中运行 slowFunction,并传递参数 5,该函数会使线程等待 5 秒钟
  • 在此过程中,主线程可以继续执行其他任务,比如打印 “Main thread doing something else…”。
  • future.waitForFinished() 会阻塞主线程直到后台任务完成

如果任务有返回值,可以通过 QFuture::result() 获取

#include <QCoreApplication>
#include <QtConcurrent>
#include <QDebug>
#include <QFuture>int computeSum(int a, int b) {qDebug() << "Computing sum in background thread...";QThread::sleep(2);  // 模拟耗时操作return a + b;
}int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 在后台线程中执行带有返回值的任务QFuture<int> future = QtConcurrent::run(computeSum, 5, 10);// 在此可以继续执行其他任务qDebug() << "Main thread is free to do other work";// 获取任务的结果int result = future.result();  // 阻塞直到任务完成qDebug() << "The result of computation is:" << result;return a.exec();
}

如果需要在任务完成后处理结果,可以使用 QFuture::watch() 或者 QFutureWatcher 来接收通知

QtConcurrent::run 是一个简单的 API,它依赖于 Qt 的线程池来管理线程,因此它适合处理不需要显式控制线程的简单任务

总结

moveToThread对比传统子类化Qthread更灵活,仅需要把你想要执行的代码放到槽,movetothread这个object到线程,然后拿一个信号连接到这个槽就可以让这个槽函数在线程里执行。可以说,movetothread给我们编写代码提供了新的思路,当然不是说子类化QThread不好,只是你应该知道还有这种方式去调用线程

轻量级的函数可以用movethread,多个短小精悍能返回快速的线程函数适用 ,无需创建独立线程类,例如你有20个小函数要在线程内做, 全部扔给一个QThread。而我觉得movetothread和子类化QThread的区别不大,更可能是使用习惯引导。又或者你一开始没使用线程,但是后边发觉这些代码还是放线程比较好,如果用子类化QThread的方法重新设计代码,将会有可能让你把这一段推到重来,这个时候,moveThread的好处就来了,你可以把这段代码的从属着movetothread,把代码移到槽函数,用信号触发它就行了。其它的话movetothread它的效果和子类化QThread的效果是一样的,槽就相当于你的run()函数,你往run()里塞什么代码,就可以往槽里塞什么代码,子类化QThread的线程只可以有一个入口就是run(),而movetothread就有很多触发的入口

以下是一些常用的QThread函数:

  • start(): 启动线程,使线程进入运行状态,调用线程的run()方法
  • run(): 线程的执行函数,需要在该函数中编写线程所需执行的任务
  • quit(): 终止线程的事件循环,在下一个事件处理周期结束时退出线程
  • wait(): 阻塞当前线程,直到线程执行完成或超时
  • finished(): 在线程执行完成时发出信号
  • terminate(): 强制终止线程的执行,不推荐使用,可能导致资源泄漏和未定义行为
  • isRunning(): 判断线程是否正在运行
  • currentThreadId(): 返回当前线程的ID
  • yieldCurrentThread(): 释放当前线程的时间片,允许其他线程执行
  • setPriority(): 设置线程优先级
  • msleep(): 让当前线程休眠指定的毫秒数

函数使用时的注意事项

  • start()函数: 调用start()函数启动线程时,会自动调用线程对象的run()方法。不要直接调用run()方法来启动线程,应该使用start()函数
  • wait()函数: wait()函数会阻塞当前线程,直到线程执行完成。在调用wait()函数时需要确保不会发生死锁的情况,避免主线程和子线程相互等待对方执行完成而无法继续
  • terminate()函数: 调用terminate()函数会强制终止线程,这样可能会导致资源未能正确释放,造成内存泄漏等问题。因此应该尽量避免使用terminate()函数,而是通过设置标志让线程自行退出
  • quit()函数: quit()函数用于终止线程的事件循环,通常与exec()函数一起使用。在需要结束线程事件循环时,可以调用quit()函数
  • finished信号: 当线程执行完成时会发出finished信号,可以连接这个信号来处理线程执行完成后的操作
  • yieldCurrentThread()函数: yieldCurrentThread()函数用于让当前线程让出时间片,让其他线程有机会执行。使用时应该注意避免过多的调用,否则会影响程序性能

多线程中的QTimer

错误用法

错误用法1

子类化QThread,在线程类中定义一个定时器,然后在run()方法中调用定时器的start()方法

TestThread::TestThread(QObject *parent): QThread(parent)
{m_pTimer = new QTimer(this);connect(m_pTimer, &QTimer::timeout, this, &TestThread::timeoutSlot);
}void TestThread::run()
{m_pTimer->start(1000);
}void TestThread::timeoutSlot()
{qDebug() << QString::fromLocal8Bit("当前线程id:") << QThread::currentThread();
}

接下来在主线程中创建该线程对象,并调用它的start()方法

m_pThread = new TestThread(this);
m_pThread->start();

执行时会报错,定时器不能被其它线程start

QObject::startTimer: Timers cannot be started from another thread

分析一下:

  • 刚开始只有主线程一个,TestThread的实例是在主线程中创建的,定时器在TestThread的构造函数中,所以也是在主线程中创建的
  • 当调用TestThread的start()方法时,这时有两个线程。定时器的start()方法是在另一个线程中,也就是TestThread中调用的

💡 创建和调用并不是在同一线程中,所以出现了错误

每个QObject实例都有一个叫做“线程关系”(thread affinity)的属性,或者说,它处于某个线程中。默认情况下,QObject处于创建它的线程中。
当QObject接收队列信号(queued signal)或者传来的事件(posted event),槽函数或事件处理器将在对象所处的线程中执行。
根据以上的原理,Qt使用计时器的线程关系(thread affinity)来决定由哪个线程发出timeout()信号。正因如此,你必须在它所处的线程中start或stop该定时器,在其它线程中启动定时器是不可能的。

错误用法2

OK,看完错误1,那是不是在run函数里面构造定时器就可以了,构造也要小心喔,别一不小心指定了父类,如下

void MyThread::run()
{timer = new QTimer(this);connect(timer,SIGNAL(timeout()),this,SLOT(timerOut()));timer->start(1000);exec();
}

执行时报错

QObject: Cannot create children for a parent that is in a different thread.
(Parent is TestThread(0x709d88), parent’s thread is QThread(0x6e8be8), current thread is TestThread(0x709d88)

因为TestThread对象是在主线程中创建的,它的QObject子对象也必须在主线程中创建。所以不能指定父对象为TestThread

错误用法3

这个问题也很容易犯,即计时器虽在线程中初始化,但在子线程非run()函数中开始start()

void MyThread::run()
{timer = new QTimer();connect(timer,SIGNAL(timeout()),this,SLOT(timerOut()));startTimer();exec();
}void MyThread::startTimer(){qDebug()<<"当前线程ID:"<<QThread::currentThreadId();timer->start(1000);
}void MyThread::timerOut()
{qDebug()<<"当前线程ID:"<<QThread::currentThreadId();
}

执行输出

前线程ID: 0x4338
当前线程ID: 0x3e20
当前线程ID: 0x3e20

run()函数线程为子线程,因此run函数中运行startTimer()为run所处线程,但因为startTimer()初始化时为主线程,因此timer->start()也随之在主线程中开始,计时器槽函数也在主线程中运行

正确用法

正确用法1

void MyThread::run()
{timer = new QTimer();connect(timer,SIGNAL(timeout()),this,SLOT(timerOut()));timer->start(1000);exec();
}void MyThread::timerOut()
{qDebug()<<"当前线程ID:"<<QThread::currentThreadId();
}

执行输出

主线程ID: 0x4458
当前线程ID: 0x4094
当前线程ID: 0x4094
当前线程ID: 0x4094

正确用法2

无需子类化线程类,通过信号启动定时器

TestClass::TestClass(QWidget *parent): QWidget(parent)
{m_pThread = new QThread(this);m_pTimer = new QTimer();m_pTimer->moveToThread(m_pThread);m_pTimer->setInterval(1000);connect(m_pThread, SIGNAL(started()), m_pTimer, SLOT(start()));connect(m_pTimer, &QTimer::timeout, this, &ThreadTest::timeOutSlot, Qt::DirectConnection);
}

通过moveToThread()方法改变定时器所处的线程,不要给定时器设置父类,否则该函数将不会生效

首先将定时器所处的线程改为新建的线程,然后连接信号槽,槽函数在定时器所处的线程中执行,注意信号槽的连接类型为Qt::DirectConnection

正确用法3

如果不想在run()函数中就开始计时器,而是在线程运行过程中根据实际情况开启(之前错误3的情况)时要怎么办呢

解决办法为:信号与槽连接中使用Qt::*DirectConnection*

信号与槽连接默认方式为:Qt::AutoConnection,因此会根据函数所处的线程自动跳转,导致错误3中的情况

void MyThread::run()
{timer = new QTimer();connect(timer,SIGNAL(timeout()),this,SLOT(timerOut()),Qt::DirectConnection);startTimer();exec();
}void MyThread::startTimer(){qDebug()<<"当前线程ID:"<<QThread::currentThreadId();timer->start(1000);
}void MyThread::timerOut()
{qDebug()<<"当前线程ID:"<<QThread::currentThreadId();
}

执行输出

主线程ID: 0x4424
当前线程ID: 0x470c
当前线程ID: 0x470c
当前线程ID: 0x470c
当前线程ID: 0x470c

Qt::ConnectionType

从上面的内容可以看到又引申出了多线程中槽函数在哪个线程中执行的问题,这里再补充说明下

同一对象的不同槽函数可以有不同的连接类型,槽函数在哪个线程被执行由连接类型、信号发射时收发双方是否在一个线程决定

类型说明

Qt::AutoConnection

默认连接类型,如果信号接收方与发送方在同一个线程,则使用Qt::DirectConnection,否则使用Qt::QueuedConnection;连接类型在信号发射时决定

Qt::DirectConnection

信号所连接至的槽函数将会被立即执行,并且是在发射信号的线程;倘若槽函数执行的是耗时操作、信号由UI线程发射,则会阻塞Qt的事件循环,UI会进入无响应状态

Qt::QueuedConnection

槽函数将会在接收者的线程被执行,此种连接类型下的信号倘若被多次触发、相应的槽函数会在接收者的线程里被顺次执行相应次数;当使用QueuedConnection时,参数类型必须是Qt基本类型,或者使用qRegisterMetaType() 进行注册了的自定义类型

Qt::BlockingQueuedConnection

和Qt::QueuedConnection类似,区别在于发送信号的线程在槽函数执行完毕之前一直处于阻塞状态;收发双方必须不在同一线程,否则会导致死锁

Qt::UniqueConnection

可以搭配以上所有连接类型使用,一经设置之后,同一信号与同一槽函数的二次连接将会失败

测试验证

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QThread>
#include <QPushButton>
#include <windows.h>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass ThreadWorker : public QObject
{Q_OBJECT
public:ThreadWorker(QObject* parent);public slots:void queuedConnect();void directConnect();
};class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:QVector<QThread*> listThread;QVector<ThreadWorker*> listWorker;Ui::Widget *ui;private slots:void on_butDirect_clicked();void on_butQueued_clicked();void on_butAuto_clicked();signals:void directConnect();void autoConnect();void queuedConnect();};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);for(int i = 0 ; i < 3 ; i++){listThread.append(new QThread(this));listWorker.append(new ThreadWorker(nullptr));}connect(this,SIGNAL(directConnect()),listWorker[0],SLOT(directConnect()),Qt::DirectConnection);connect(this,SIGNAL(autoConnect()),listWorker[2],SLOT(directConnect()),Qt::AutoConnection);connect(this,SIGNAL(queuedConnect()),listWorker[1],SLOT(queuedConnect()),Qt::QueuedConnection);connect(this,SIGNAL(directConnect()),listWorker[1],SLOT(directConnect()),Qt::QueuedConnection);for(int  i = 0 ; i < 3 ; i++ )listWorker[i]->moveToThread(listThread[i]);for(auto i : listThread) i->start();qDebug()<<"The main thread id is "<< QThread::currentThreadId();}Widget::~Widget()
{delete ui;for(auto i : listThread)i->exit();
}void Widget::on_butDirect_clicked()
{emit directConnect();
}void Widget::on_butQueued_clicked()
{emit queuedConnect();
}void Widget::on_butAuto_clicked()
{emit autoConnect();}ThreadWorker::ThreadWorker(QObject *parent) : QObject(parent)
{}void ThreadWorker::queuedConnect()
{for(int i  = 0 ; i <10 ; i ++)Sleep(100);qDebug()<<"The kid thread id is "<< QThread::currentThreadId();}
void ThreadWorker::directConnect()
{for(int i  = 0 ; i <10 ; i ++)Sleep(100);qDebug()<<"The kid thread id is "<< QThread::currentThreadId();}

执行输出

The main thread id is  0x30b0
The kid thread id is  0x30b0
The kid thread id is  0x7cc
The kid thread id is  0x7cc
The kid thread id is  0x24c0

小结

这里参考学习了多篇博客,对Qt的多线程实现,以及如何在多线程场景中使用QTimer有了更深的认识,同时对多线程中槽函数的调用也有了新的认识。希望和大家一起进步

相关文章:

Qt多线程与QTimer详解

引用 Qt多线程中使用QTimer&#xff08;常见问题汇总&#xff09; [多线程]多线程使用QTimer Qt::ConnectionType&#xff1a;Qt不同类型connect的详细区别说明与应用 Qt的4种多线程实现方式 一文搞定之Qt多线程(QThread、moveToThread) QTimer The QTimer class provides repe…...

基于stm23的智慧宿舍系统 (DAY10)_小程序

好久没记录开发进度了&#xff0c;今天小程序差不多开发完了&#xff0c;UI这块算是比较常见了&#xff0c;主要功能是能连接onenet查看设备上传的数据&#xff0c;同时也能对设备进行一些控制下面是几个主要的函数&#xff0c;功能比较简单 wx.request({url: ${apiBaseUrl}/t…...

深入理解Spring事务

目录 什么是Spring事务为什么需要Spring事务Spring事务的实现 Spring事务的传播机制Spring事务的底层原理 EnableTransactionManagement --开启Spring管理事务Import(TransactionManagementConfigurationSelector.class) --提供两个beanAutoProxyRegistrar --启用AOP的功能&am…...

Ubuntu22.04深度学习环境安装【Anaconda+Pycharm】

anaconda可以提供多个独立的虚拟环境&#xff0c;方便我们学习深度学习&#xff08;比如复现论文&#xff09;&#xff1b; Pycharm编辑器可以高效的编写python代码&#xff0c;也是一个很不错的工具。 下面就记录下Ubuntu22.04的安装流程&#xff1a; 1.Anaconda安装 下载Ana…...

五、docker的网络模式

五、docker的网络模式 5.1 Docker的四种网络模式 当你安装docker时&#xff0c;它会自动创建三个网络&#xff0c;可使用如下命令查看&#xff1a; [rootlocalhost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 7390284b02d6 bridge bridge lo…...

使用el-row和el-col混合table设计栅格化,实现表头自适应宽度,表格高度占位

演示效果&#xff1a; 如上图,由于地址信息很长,需要占多个格子,所以需要错开,若想实现这种混合效果,可以这样搭建: 页面效果&#xff1a; 代码分析: 上面使用el-row和el-col搭建表单显示 第一排三个8,第二排8和16 下面混合table实现&#xff0c;并使用border来自适应宽度…...

【服务器监控】grafana+Prometheus+node exporter详细部署文档

我们在进行测试时&#xff0c;不可能一直手动看着服务器的性能消耗&#xff0c;这时候就需要有个工具替我们监控服务器的性能消耗。这里记录下grafanaPrometheusnodeExporter的组合用于监控服务器。 简单介绍&#xff1a; grafana&#xff1a;看板工具&#xff0c;所有采集的…...

JavaScript中todolist操作--待办事项的添加 删除 完成功能

效果图 在文本框中输入内容点击添加按钮会在下面生成 添加功能 html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&qu…...

Windows中MySQL8.3.4 MSI版本——详细安装教程

一、下载MySQL安装文件。 下载地址&#xff1a;MySQL官网 进入后点击下面的MySQL社区版下载 点击MySQL Comunity Server。 我这里选择的是版本8.4.3LTS版本&#xff0c;在线对应的msi文件。 点击No thanks,直接下载。 二、安装MySQL 2.1、双击刚刚下载好的msi文件&#xff0c;…...

MySQL-DDL之数据库操作

文章目录 一. 创建数据库1. 直接创建数据库&#xff0c;如果存在则报错2. 如果数据库不存在则创建3. 创建数据库时设置字符集4. 栗子 二. 查看数据库1. 查看数据库 三. 删除数据库1. 删除数据库 四. 使用数据库1. 使用数据库2. 查看正在使用的数据库 数据定义语言&#xff1a;简…...

Python 笔记之进程通信

当需要创建的子进程数量不多时&#xff0c;可以直接利用multiprocessing中的Process动态生成多个进程 但是如果是上百个或者上千个目标&#xff0c;手动去创建进程的工作量很大&#xff0c;此时就可以利用到Multiprocessing模块提供的Pool方法 初始化pool时&#xff0c;可以指定…...

【Transformer序列预测】Pytorch中构建Transformer对序列进行预测源代码

Python&#xff0c;Pytorch中构建Transformer进行序列预测源程序。包含所有的源代码和数据&#xff0c;程序能够一键运行。此程序是完整的Transformer&#xff0c;即使用了Encoder、Decoder和Embedding所有模块。源程序是用jupyterLab所写&#xff0c;建议分块运行。也整理了.p…...

生产者-消费者模式:多线程并发协作的经典案例

生产者-消费者模式是多线程并发编程中一个非常经典的模式&#xff0c;它通过解耦生产者和消费者的关系&#xff0c;使得两者可以独立工作&#xff0c;从而提高系统的并发性和可扩展性。本文将详细介绍生产者-消费者模式的概念、实现方式以及应用场景。 1 生产者-消费者模式概述…...

数据库-mysql(基本语句)

演示工具&#xff1a;navicat 连接&#xff1a;mydb 一.操作数据库 1.创建数据库 ①create database 数据库名称 //普通创建 ②create database if not exists 数据库名称 //创建数据库&#xff0c;判断不存在&#xff0c;再创建&#xff1a; 使用指定数据库 use 数据库…...

android12L super.img 解压缩及其挂载到ubuntu18.04

本文介绍如何在Ubuntu18.04上解压缩高通平台Android12L的super.img&#xff0c;并将其挂载到系统中查看内容。 在源码的根目录下&#xff0c;执行如下命令&#xff1a; out/host/linux-x86/bin/simg2img out/target/product/msmnile_gvmq/super.img super.img_rawmkdir super…...

flask简易版的后端服务创建接口(python)

1.pip install安装Flask和CORS 2.创建http_server.py文件,内容如下 """ ============================ 简易版的后端服务 ============================ """ from flask import Flask, request, jsonify from flask_cors import CORS app = F…...

小程序入门学习(四)之全局配置

一、 全局配置文件及常用的配置项 小程序根目录下的 app.json 文件是小程序的全局配置文件。常用的配置项如下&#xff1a; pages&#xff1a;记录当前小程序所有页面的存放路径 window&#xff1a;全局设置小程序窗口的外观 tabBar&#xff1a;设置小程序底部的 tabBar 效…...

PHP使用RabbitMQ(正常连接与开启SSL验证后的连接)

代码中包含了PHP在一般情况下使用方法和RabbitMQ开启了SSL验证后的使用方法&#xff08;我这边消费队列是使用接口请求的方式&#xff0c;每次只从中取出一条&#xff09; 安装amqp扩展 PHP使用RabbitMQ前&#xff0c;需要安装amqp扩展&#xff0c;之前文章中介绍了Windows环…...

轻量级视觉骨干网络 MobileMamba: Lightweight Multi-Receptive Visual Mamba Network

MobileMamba 快速链接解决问题&#xff1a;视觉模型在移动设备端性能和效果的平衡性解决方法&#xff1a;改进网络结构训练和测试策略网络结构改进训练和测试策略 实验支撑&#xff1a;图像分类、分割&#xff0c;目标检测等图像分类结果对比目标检测和实例分割结果对比语义分割…...

科技云报到:数智化转型风高浪急,天翼云如何助力产业踏浪而行?

科技云报到原创。 捷径消亡&#xff0c;破旧立新&#xff0c;是今年千行百业的共同底色。 穿越产业周期&#xff0c;用数字化的力量重塑企业经营与增长的逻辑&#xff0c;再次成为数字化技术应用的主旋律&#xff0c;也是下一阶段产业投资的重点。 随着数字化转型行至“深水区…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

Bean 作用域有哪些?如何答出技术深度?

导语&#xff1a; Spring 面试绕不开 Bean 的作用域问题&#xff0c;这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开&#xff0c;结合典型面试题及实战场景&#xff0c;帮你厘清重点&#xff0c;打破模板式回答&#xff0c…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化

iOS 应用的发布流程一直是开发链路中最“苹果味”的环节&#xff1a;强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说&#xff0c;这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发&#xff08;例如 Flutter、React Na…...