Qt中的多线程使用

2023-11-15

Qt提供了许多用于处理线程的类和函数,我们可以在从其中选择一种合适的来实现。总结下来一共有4种:

  1. QThread
  2. QThreadPool and QRunnable

  3. Qt Concurrent

  4. WorkerScript (QML)

下面就通过示例来展示每种方式的使用。示例是参考Qt帮助文档,在Qt5.9.8 MSVC2015下运行,没有考虑线程同步的情况。

一、QThread的使用

QThread是Qt中所有线程控制的基础。每个QThread实例表示并控制一个线程。QThread的使用有2种方式:

  1. 继承QThread,重写run。
  2. QObject::moveToThread。

1.继承QThread,重写run

使用这种方式的时候,QThread实例存在于实例化它的旧线程中,而run()在新线程中执行。这意味着QThread的槽都将在旧线程中执行。如下面代码中的WorkerThread::thread接口,打印的是构造WorkerThread的线程,而run里面打印的新的线程。即使我通过connect接口连接这个槽也是一样的,比如MainWindow中有一个按钮,把按钮的clicked信号的workerThread的thread连接起来。

class WorkerThread : public QThread
{
    Q_OBJECT
public:
    WorkerThread(QObject *parent);
public slots:
    void printThread();
    void stop();
signals:
    void resultReady(const QString &result);

    // QThread interface
protected:
    virtual void run() override;

private:
    bool m_stop;
};

WorkerThread::WorkerThread(QObject *parent)
    : QThread (parent)
    ,  m_stop(false)
{

}

void WorkerThread::printThread()
{
    qDebug() << "Thread1:" << currentThread();
}

void WorkerThread::stop()
{
    m_stop = true;
}

void WorkerThread::run()
{
    qDebug() << "Thread2:" << currentThread();
    while (!m_stop) {
        static int i = 0;
        emit resultReady(QString::number(i++));
        msleep(1000);
    }
}


///
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    WorkerThread *workerThread = new WorkerThread(this);
    connect(workerThread, &WorkerThread::resultReady, this, &MainWindow::handleResults);
    workerThread->start();
    workerThread->thread();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::handleResults(const QString &result)
{
    qDebug() << "result:" << result;
}

2.QObject::moveToThread

在这种方式中,Worker的槽函数中的代码将在一个单独的线程中执行。但是,您可以自由地将Worker的槽函数连接到任何线程中任何对象的任何信号。由于队列连接的机制,跨不同线程连接信号和槽是安全的。在下面的这个示例中,MainWindow::MainWindow中打印的线程和Worker::thread中打印的线程是不一样的。也就是说Worker::thread中的代码是在另外一个线程中执行的。

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);

public slots:
    void thread();
};

Worker::Worker(QObject *parent) : QObject(parent)
{

}

void Worker::thread()
{
    qDebug() << QThread::currentThread();
}


/
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QThread *workerThread = new QThread(this);
    Worker *worker = new Worker;
    worker->moveToThread(workerThread);
    connect(ui->pushButton, &QPushButton::clicked, worker, &Worker::thread);
    connect(workerThread, &QThread::finished, worker, &QObject::deleteLater);
    qDebug() << QThread::currentThread();
    workerThread->start();
}

二、QThreadPool and QRunnable

频繁地创建和销毁线程可能代价高昂。为了减少这种开销,可以将现有线程重新用于新任务。QThreadPool是可重复使用的QThread的集合。

要在QThreadPool的某个线程中运行代码,请重新实现QRunnable::run()并实例化子类QRunnable。使用QThreadPool::start()将QRunnable放入QThreadPool的运行队列中。当一个线程可用时,QRunnable::run()中的代码将在该线程中执行。

每个Qt应用程序都有一个全局线程池,可以通过QThreadPool::globalInstance()访问。该全局线程池根据CPU中的内核数量自动维护最佳线程数量。但是,可以显式创建和管理单独的QThreadPool。(自Qt帮助文档)

在下面的示例代码中,MainWindow::MainWindow中打印的线程和HelloWorldTask中打印的线程不一样。

class HelloWorldTask : public QRunnable
 {
     void run()
     {
         qDebug() << QThread::currentThread();
     }
 };

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    HelloWorldTask *hello = new HelloWorldTask();
    qDebug() <<  QThread::currentThread();
    // QThreadPool takes ownership and deletes 'hello' automatically
    QThreadPool::globalInstance()->start(hello);
}

三、Qt Concurrent

QtConcurrent命名空间提供了高级API,可以在不使用诸如互斥、读写锁、等待条件或信号量等低级线程原语的情况下编写多线程程序。使用QtConcurrent编写的程序会根据可用的处理器内核数量自动调整使用的线程数量。这意味着,今天编写的应用程序在未来部署在多核系统上时将继续扩展。(自Qt帮助文档)

1.QtConcurrent::map()

QFuture<void> map(Sequence &sequence, MapFunction function)
QFuture<void> map(Iterator begin, Iterator end, MapFunction function)

将一个函数应用于容器中的每个项,并将运算结果替换原来的元素。

static void add(int &a)
{
    a = a + 2;
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QList<int> v = {1, 2, 3, 4, 5};
    qDebug() << v;
    QFuture<void> result = QtConcurrent::map(v, add);
    result.waitForFinished();
    qDebug() << v;
}



输出:
(1, 2, 3, 4, 5)
(3, 4, 5, 6, 7)

2.QtConcurrent::mapped()

QFuture<T> mapped(const Sequence &sequence, MapFunction function)
QFuture<T> mapped(ConstIterator begin, ConstIterator end, MapFunction function)

功能类似 map() 函数,它会返回一个新容器存储函数处理后的结果。

static int add(const int &a)
{
    return a + 2;
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QList<int> v = {1, 2, 3, 4, 5};
    qDebug() << v;
    QFuture<int> result = QtConcurrent::mapped(v, add);
    result.waitForFinished();
    qDebug() << v;
    qDebug() << result.results();
}

//
输出:
(1, 2, 3, 4, 5)
(1, 2, 3, 4, 5)
(3, 4, 5, 6, 7)

3.QtConcurrent::mappedReduced() 

QFuture<T> mappedReduced(const Sequence &sequence, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce)
QFuture<T> mappedReduced(ConstIterator begin, ConstIterator end, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce)

功能类似 mapped() 函数,按顺序为每个项目调用mapFunction一次。每个mapFunction的返回值被传递给reduceFunction。

static int add(const int &a)
{
    return a + 2;
}

static void addSum(int &a, const int &b)
{
    a = a + b;
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QList<int> v = {1, 2, 3, 4, 5};
    qDebug() << v;
    QFuture<int> result = QtConcurrent::mappedReduced(v, add, addSum);
    result.waitForFinished();
    qDebug() << result.result();
}


输出:
(1, 2, 3, 4, 5)
25

4.QtConcurrent::filter()

QFuture<void> filter(Sequence &sequence, FilterFunction filterFunction)

根据筛选函数的结果从容器中删除所有项目。

static bool isOdd(const int &a)
{
    return a % 2 != 0;
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QList<int> v = {1, 2, 3, 4, 5};
    qDebug() << v;
    QFuture<void> result = QtConcurrent::filter(v, isOdd);
    result.waitForFinished();
    qDebug() << v;
}


输出:
(1, 2, 3, 4, 5)
(1, 3, 5)


5.QtConcurrent::filtered()

QFuture<T> filtered(const Sequence &sequence, FilterFunction filterFunction)
QFuture<T> filtered(ConstIterator begin, ConstIterator end, FilterFunction filterFunction)

功能类似filter(), 将结果以新的容器返回。

static bool isOdd(const int &a)
{
    return a % 2 != 0;
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QList<int> v = {1, 2, 3, 4, 5};
    qDebug() << v;
    QFuture<int> result = QtConcurrent::filtered(v, isOdd);
    result.waitForFinished();
    qDebug() << v;
    qDebug() << result.results();
}

/
输出:
(1, 2, 3, 4, 5)
(1, 2, 3, 4, 5)
(1, 3, 5)

6.QtConcurrent::filteredReduced()

QFuture<T> filteredReduced(const Sequence &sequence, FilterFunction filterFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce)
QFuture<T> filteredReduced(ConstIterator begin, ConstIterator end, FilterFunction filterFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce)

功能类似filtered,把结果处理成一个单一的值。

static void addSum(int &a, const int &b)
{
    a = a + b;
}

static bool isOdd(const int &a)
{
    return a % 2 != 0;
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QList<int> v = {1, 2, 3, 4, 5};
    qDebug() << v;
    QFuture<int> result = QtConcurrent::filteredReduced(v, isOdd, addSum);
    result.waitForFinished();
    qDebug() << result.result();
}

/
输出:
(1, 2, 3, 4, 5)
9

上面的每个接口都有一个blockingXXX的接口,比如blockingMap、blockingMapped。这些接口将被阻止,直到处理完序列中的所有项目为止。

7.QtConcurrent::run()

QFuture<T> run(Function function, ...)
QFuture<T> run(QThreadPool *pool, Function function, ...)

第一个接口等价

QtConcurrent::run(QThreadPool::globalInstance(), function, ...);
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QtConcurrent::run([]{
        qDebug() << "11111111111";
    });

    QtConcurrent::run(std::bind(&MainWindow::handleResults, this, "Test"));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::handleResults(const QString &result)
{
    qDebug() << "result:" << result;
}

//
输出:
11111111111
result: "Test"

四、WorkerScript

WorkerScript QML类型允许JavaScript代码与GUI线程并行运行。

每个WorkerScript实例都可以附加一个.js脚本。当调用WorkerScript::sendMessage()时,该脚本将在一个单独的线程(和一个独立的QML上下文)中运行。当脚本完成运行时,它可以向GUI线程发送一个回复,GUI线程将调用WorkerScript::onMessage()信号处理程序。使用WorkerScript类似于使用QObject::moveToThread。数据通过信号在线程之间传输。

import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    Text {
        id: myText
        text: 'Click anywhere'
    }

    WorkerScript {
        id: myWorker
        source: "script.js"

        onMessage: myText.text = messageObject.reply
    }

    MouseArea {
        anchors.fill: parent
        onClicked: myWorker.sendMessage({ 'x': mouse.x, 'y': mouse.y })
    }
}



//script.js
WorkerScript.onMessage = function(message) {
     // ... long-running operations and calculations are done here
     WorkerScript.sendMessage({ 'reply': 'Mouse is at ' + message.x + ',' + message.y })
 }

当用户单击矩形内的任何位置时,将调用sendMessage(),从而触发script.js中的WorkerScript.onMessage()处理程序。这反过来会发送一条回复消息,然后由myWorker的onMessage()处理器接收。

由于WorkerScript.onMessage()函数是在单独的线程中运行的,因此JavaScript文件是在独立于主QML引擎的上下文中的。这意味着,与导入QML的普通JavaScript文件不同,上例中的script.js无法访问QML项的属性、方法或其他属性,也无法通过QQmlContext访问QML对象上设置的任何上下文属性。

此外,可以传递给脚本和从脚本传递的值的类型也有限制。有关详细信息,请参阅sendMessage()文档。

五、选择合适的方法

如上所述,Qt为开发线程应用程序提供了不同的解决方案。给定应用程序的正确解决方案取决于新线程的用途和线程的使用寿命。下面是Qt的线程技术的比较,然后是一些示例用例的推荐解决方案。

QThread

QRunnable and QThreadPool

QtConcurrent::run()

Qt Concurrent (Map, Filter, Reduce)

WorkerScript

语言


C++
 
C++ C++ C++ QML
是否可以指定线程优先级 Y Y
是否可以运行事件循环 Y
线程是否可以通过信号接收数据更新 Y Y
是否可以使用信号控制线程 Y Y
线程是否可以通过QFuture进行监控 部分 Y
内置暂停/恢复/取消功能 Y

六、用例示例

线程周期 操作 解决方案
一次调用 在另一个线程中运行一个新的线性函数,可以选择在运行过程中更新进度。

Qt提供了不同的解决方案:

将函数放在重新实现的QThread::run()中,然后启动QThread。发出信号以更新进度。或

将函数放在重新实现的QRunnable::run()中,并将QRunnable添加到QThreadPool中。写入线程安全的变量以更新进度。或

使用QtConcurrent::Run()运行函数。写入线程安全的变量以更新进度。

一次调用 在另一个线程中运行现有函数并获取其返回值 使用QtConcurrent::Run()运行函数。当函数返回时,让QFutureWatcher发出finished()信号,并调用QFutureWatcher::result()来获取函数的返回值。
一次调用 使用可使用的所有的核对一个容器所有项执行操作。例如,从图像列表中生成缩略图。 使用QtCurrent的QtCurrent::filter()函数来选择容器元素,使用QtConcurrent::map()函数对每个元素应用一个操作。要将输出合并为一个结果,请改用QtConcurrent::filteredReduced()和QtConcCurrent::mappedReduced()。
一次调用/永久 在纯QML应用程序中执行长时间计算,并在结果准备就绪时更新GUI。 将计算代码放在.js脚本中,并将其附加到WorkerScript实例。调用sendMessage()在新线程中启动计算。让脚本也调用WorkerScript::sendMessage(),将结果传递回GUI线程。在onMessage中处理结果,并在那里更新GUI。
永久 一个QObject存在另一个线程中,该线程可以根据请求执行不同的任务和/或接收新的数据进行处理。 对QObject进行子类化以创建worker。实例化此worker对象和一个QThread。将worker移动到新线程。通过队列连接的信号槽连接向工作对象发送命令或数据。             
永久 在另一个线程中重复执行复杂的操作,该线程不需要接收任何信号或事件。 直接在重新实现QThread::run()的中编写无限循环。启动线程,不需要事件循环。让线程发出信号,将数据发送回GUI线程。

第五和第六来自Qt的版本文档Thread Support in Qt。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt中的多线程使用 的相关文章

  • 如何访问Loader的sourceComponent中的QML对象?

    我可能需要读取或写入的一些属性Loader s sourceComponent来自一些外部函数 访问该房产的方式是什么x里面的对象的Loader s sourceComponent import QtQuick 2 0 Item width
  • 如何在qt中创建正确的退出按钮

    我正在尝试创建一个退出按钮来正确关闭我在 QT 中制作的 GUI 我尝试通过以下方式执行此操作 include
  • 常量类成员、赋值运算符和 QList

    请确认我是否正确并告诉我是否有更好的解决方案 我了解具有常量成员的对象 例如int const width 无法由编译器隐式创建的合成赋值运算符处理 但是 QList 我想 std list 也是如此 需要一个有效的赋值运算符 因此 当我想
  • Qt:删除富文本

    对于明文有QFontMetrics elideText https doc qt io qt 5 qfontmetrics html elidedText https doc qt io qt 5 qfontmetrics html eli
  • QWebSocketServer - 不释放内存

    首先 我在安全 websocket 服务器应用程序上运行 valgrind 并发现了一个问题 在 Qt Memcheck 中我必须检查 外部错误 看到它 一些字节是肯定输了 指着我的main就在我的地方QCoreApplication ex
  • 扩展 TabViewStyle styleData

    我目前正在尝试找到一种更好的方法来执行此操作 将图标添加到选项卡 现在 我正在放弃 styleData title 以包含图标源 但如果能够扩展 styleData 就更好了 这样我就可以包含其他自定义属性 这是我当前的黑客 Tab tit
  • 面向 Delphi 开发人员的 Qt

    有人知道为 Delphi C Builder VCL 开发人员解释 Qt 的书籍或教程吗 对于具有该背景的开发人员来说 学习 Qt 的最佳方法是什么 我对如何使用 Qt 完成我知道如何在 Delphi 中完成的事情特别感兴趣 例如 Qt 相
  • Qt 图表和数据可视化小部件

    我已经安装了 Qt 5 7 来尝试 Qt 图表和 Qt 数据可视化 但我在 Qt Designer 和 Qt Creator 中都找不到新的小部件 有什么建议我应该做什么才能让新的小部件出现在设计器中 我今天遇到了完全相同的问题 默认情况下
  • 如何在 Qt-Creator 中添加自定义构建步骤?

    构建我的应用程序后 我想将其复制到特定目录 在 Windows 7 上 自定义构建步骤 cmd exe c k copy MyPlugin dll HostApp Debug plugins 但我有错误 Can t run process
  • 从 Qt 更改屏幕分辨率?

    我想更改屏幕分辨率 然后使用一个 ActiveX 控件 Flash 播放器 进入全屏 显然 仅适用于 Windows 的解决方案就可以了 有 Qt api 吗 还是我需要深入研究 winapi 如果是这样 我该在哪里查找 关键字 谢谢您的帮
  • 如何在不同的QT线程中创建一个窗口?

    我有一个应用程序 其中每个线程 主线程除外 都需要创建自己的窗口 我尝试创建一个线程然后调用this gt exec in the run功能 然而 在我接到那个电话之前我就收到了一个错误 ASSERT failure in QWidget
  • QMessageBox::about 可以根据标题长度调整大小吗?

    我想创建一个简单的 关于 对话框 但注意到QMessageBox about不会根据标题的长度调整其大小 由于字体较大 标题通常会更长 至少在我的桌面环境中 仅根据内容调整其大小 有没有办法确保对话框足够大以显示所有标题 我当然可以向 ab
  • QFileDialog 作为 TableView 的编辑器:如何获取结果?

    我正在使用一个QFileDialog作为某些专栏的编辑QTableView 这基本上有效 对一些焦点问题取模 请参阅here https stackoverflow com questions 22854242 qfiledialog as
  • MSVC C4100:“应用程序”:未引用的形式参数警告

    使用 MSVC 编译时 以下代码会生成 C4100 警告 这怎么可能 显然引用了 应用程序 class ApplicationInfo public QObject Q OBJECT public static void initializ
  • 为什么anchors.centerIn不适用于Column元素?

    我有这段 QML 代码 Column spacing units gu 2 anchors fill parent centerIn parent Row spacing units gu 4 Row spacing units gu 4
  • 在 QtCreator 中调试时如何查看 qDebug 消息

    我正在从 Eclipse CDT 带有 Qt 集成插件 过渡到 QtCreator 2 0 但仍有一件事困扰着我 QtCreator 当我在 QtCreator 中调试时 我在Application output tab直到我停止正在调试的
  • qvariant 作为 qhash 中的键

    我想创建一个带有 QVariants 键的数据结构 它看起来像这样 QHash
  • 如何从键盘为 QTableWidget 创建信号?

    我有一张桌子 可以通过左 右 上 下按钮在里面移动 现在 当我停留在某个单元格并按空格键时 我需要创建一个信号 该信号还应该带来该单元格的坐标 我尝试使用 QTableWidget 的标准信号 但它不起作用 我该如何解决这个问题 创建一个单
  • Qt - 意外的 GDB 退出

    我正在尝试开发一个应用程序 该应用程序创建图像并使用双线性插值填充颜色像素 然后显示它 到目前为止我的代码如下 include
  • QGraphicsView 在完整布局中未最大化

    I have following GUI having four QGraphicView objects 正如您在每个视图下看到的那样 它有四个工具按钮 为了最大化视图 我连接了工具按钮的信号来隐藏其他三个视图的插槽 并将大小策略设置为扩

随机推荐

  • hdu 6208 The Dominator of Strings

    Problem acm hdu edu cn showproblem php pid 6208 Meaning 有 n 个字符串 问是否能找到其中一串 使得其它串都是它的子串 Analysis 如果存在这个串 那它一定是 n 个中的最长串
  • LeetCode刷题记录 字节跳动题库

    1 两数之和 哈希 一遍遍历 3 无重复字符的最长子串 哈希 流动窗口 双指针 因为右端点的位置一定不会朝左边走 建议再看看同类型的题目 2 两数相加 题 42 接雨水 单调递减栈 核心思想 对于每个点找其左边和右边第一个大于或等于它的点
  • 程序员最美的情人节玫瑰花,JAVA代码实现的3D玫瑰噢

    用纯javascript脚本编写的神奇3D圣诞树 令人印象深刻 2月14日情人节就要来临了 还是Roman Cortes 这次他又带来了用javascript脚本编写的红色玫瑰花 用代码做出的玫瑰花 这才是牛逼程序员送给女友的最好情人节礼物
  • idea自动去除导入但未使用的包

    使用idea开发过程中通常我们可能会引入某个包使用但是在后续更改中这个包就不需要了 一个个去除很麻烦 他每个java文件去除的快捷键是ctrl shift o 如果想要更智能的方法我们可以做如下配置 1 使用ctrl alt s进入sett
  • 【机器学习-分类】决策树预测

    我用一些机器学习的算法对数据进行一个分类 下面是一些需要用到的基础代码 以决策树为例 并不包括针对项目的模型处理和修改 留作记忆学习 对于数据划分训练集直接省略 def Tree score depth 3 criterion entrop
  • 论文笔记 Graph Attention Networks

    2018 ICLR 1 intro 1 1 GCN的不足 无法完成inductive任务 inductive任务是指 训练阶段与测试阶段需要处理的graph不同 通常是训练阶段只是在子图上进行 测试阶段需要处理未知的顶点 GGN 的参数依赖
  • SQL注入1(联合注入)

    数据库基础 一 数据库的操作 1 基本语句 mysql u xxx 进入mysql show databases 查看所有库名 use XXX 进入某个库 show tables 查看库的表 查询语句 select 数据操作语句 inser
  • echarts分割柱形图实现渐变电量效果柱状图

    先看下效果图是这个样子的 和普通的柱状图最明显的区别就是需要做成类似于电池格电量显示效果 目录 1 官网找例子 2 改造示例 3 全部代码 4 初始效果和完成效果对比 1 官网找例子 首先到Echarts官网找到基础的柱状图 官网初始opt
  • ZABBIX实践(三) window下的Agent端部署以及服务端汉化

    Zabbix 可以监控的对象非常多 支持的操作系统也很多 主流的linux和windows是平时接触最多的系统 本节说明一下zabbix 在windows下如何安装和配置 1 目标 1 在windows上安装zabbix agent 并且进
  • 用python做透视表_python制作数据透视表pivot_table方法详解

    数据透视表 Pivot Table 是一种交互式的表 可以进行某些计算 如求和与计数等 所进行的计算与数据跟数据透视表中的排列有关 之所以称为数据透视表 是因为可以动态地改变它们的版面布置 以便按照不同方式分析数据 也可以重新安排行号 列标
  • 3.2.spring boot 日志框架logback使用示例

    给类路径下放上每个日志框架自己的配置文件即可 SpringBoot就不使用他默认配置 slf4j 的了 logback xml 直接可被日志框架识别 logback spring xml 日志框架就不直接加载日志的配置项 由SpringBo
  • fx5u 脉冲输出指令PLSY(DPLSY)4种写法

    本文描述三菱FX5U的 脉冲输出指令PLSY DPLSY 4种写法 都有效 第一行 设置脉冲输出频率 第二行 DPLSY D0 K0 K1 FX5U 第二个参数是脉冲数量 设置为K0表示一值输出脉冲 第三个参数是轴号K1 表示Y0脉冲输出
  • C语言——数据在内存中的存储(上)

    数据在内存中的存储 1 数据类型的介绍 之前已经介绍过C语言中的基本数据类型了 主要有 char 字符数据类型 short 短整型 int 整形 long 长整型 long long 更长的整形 float 单精度浮点数 double 双精
  • Win10下AI CC 2019安装教程(超级详细-小白版)

    下载 Adobe Illustrator CC 2019 百度网盘下载地址 链接 https pan baidu com s 1i5MeAOu8 wrSep0nOy8OCA 提取码 k9gm 打开上面链接 用百度网盘软件下载 安装 右键压缩
  • 设计模式:建造者模式

    无论是在现实世界中还是在软件系统中 都存在一些复杂的对象 它们拥有多个组成部分 如汽车 它包括车轮 方向盘 发送机等各种部件 而对于大多数用户而言 无须知道这些部件的装配细节 也几乎不会使用单独某个部件 而是使用一辆完整的汽车 可以通过建造
  • NoSQL数据库简介

    目录 1 NoSQL数据库概述 2 NoSQL适用场景 3 NoSQL不适用场景 4 缓存数据库 1 Memcached 2 Redis 3 mongoDB 5 列式数据库 1 行式存储数据库和列式存储数据库 1 行式存储数据库 2 列式存
  • Redis Hyperloglog(浅析)

    在Redis中 每个HyperLogLog键只需要花费12KB内存 就可以计算接近2 64个不同的基数 HyperLogLog只能统计基数的大小 也就是数据集的大小 集合的个数 他不能存储元素的本身 不能向set集合那样存储元素本身 也就是
  • mybatis实战教程(mybatis in action),mybatis入门到精通

    原文地址 http blog csdn net techbirds bao article details 9233599 这个mybatis教程也不错 http limingnihao iteye com blog 781671 MyBa
  • String和StringBuilder、StringBuffer

    1 Srring 对于String来说 是把数据存放在了常量池中 因为所有的String 默认都是以常量形式保存 且由final修饰 因此在线程池中它是线程安全的 因为每一个String当被创建好了以后 他就不再发生任何变化 但是它的执行速
  • Qt中的多线程使用

    Qt提供了许多用于处理线程的类和函数 我们可以在从其中选择一种合适的来实现 总结下来一共有4种 QThread QThreadPool and QRunnable Qt Concurrent WorkerScript QML 下面就通过示例