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中的多线程使用 的相关文章

  • 如何将 QByteArray 转换为十六进制字符串?

    我有打击QByteArray QByteArray ba ba 0 0x01 ba 1 0x10 ba 2 0x00 ba 3 0x07 我真的不知道如何将此 QByteArray 转换为结果字符串 其中包含 01100007 我将使用 Q
  • 在 Qt 5 中嵌入 Python

    我想将 Python 解释器嵌入到 Qt 5 应用程序中 我在 Qt 5 中有一个工作应用程序 但是当我把 include
  • 如何从浮点数组创建新的 QImage

    我有一个代表图像的浮点数数组 列在前 我想在 QGraphicsSecene 上将图像显示为 QPixmap 为了做到这一点 我尝试使用 QImage 构造函数 QImage const uchar data int width int h
  • 我应该使用 QCoreApplication::processEvents() 还是 QApplication::processEvents()?

    我有一个从两者调用的方法QThreads和主线程 这个方法有时可能需要很长时间才能在循环中进行计算 所以我把QCoreApplication processEvents 这可以防止 GUI 冻结 在某个时刻我已经改变了QCoreApplic
  • PyQt - 如何从给定的小部件获取顶级父级?

    给定的小部件如何访问它最旧的父级 即顶级小部件 我需要 showMinimized it 现在我正在使用 self parent parent parent showMinimized 但这似乎不是最好的方法 如果它移动了 我需要手动更改父
  • 如何在 Qt Creator 中编辑 QtWebKit 的右键上下文菜单?

    好吧 这是我的困境 我正在使用 Qt Creator 制作一个使用 Webkit 的简单应用程序 我认为 Qt Creator 会有一种简单的方法来使用信号和槽编辑器编辑右键单击上下文菜单 但事实证明这不是真的 我知道 webkit 有与上
  • 用 C++/Qt 编写的程序中的 RTF / doc / docx 文本提取

    我正在写一些程序Qt https en wikipedia org wiki Qt 28software 29 C 我需要从中读取文本微软Word https en wikipedia org wiki Microsoft Word RTF
  • QMutex 是否需要是静态的,以便此类实例的其他线程调用知道暂停其操作?

    从多个线程调用以下附加函数 我不希望数据重写附加 因为计数器尚未增加 除了当前使用 Append 的线程之外 这是否会挂起所有进入的线程 或者其他线程会继续运行而不追加数据吗 互斥锁是否需要是 静态 的 或者每个实例都知道要暂停操作吗 如果
  • 在没有加载器的情况下实例化内联组件

    有没有办法实例化内联Component 即在同一文件中定义 而不使用Loader 我不太关心使用的性能影响Loader因为我要用很多东西污染我的文件Loader包装纸 我发现从 JavaScript 创建动态 QML 对象 http doc
  • QStyledItemDelegate 绘制自定义小部件失败

    在我的一个项目中 我使用的是QTableWidget为了显示一些复杂的计算结果 为了提高表格的可读性 我需要在单个表格单元格内显示两个对齐的值 后来我想通过使用颜色或箭头等来进一步自定义小部件 为此我源自QStyledItemDelegat
  • QMainWindow 上的 Qt 布局

    我设计了一个QMainWindow with QtCreator s设计师 它由默认的中央小部件 aQWidget 其中包含一个QVBoxLayout以及其中的所有其他小部件 现在我想要的一切就是QVBoxLayout自动占据整个中央小部件
  • 如何在Qt 5中的paintEvent上使用mouseMoveEvent?

    我是 Qt 和 c 的新手 所以我遇到了一些困难 我正在尝试创建一个小部件 它可以获取 mouseMoveEvent 位置并在鼠标位置的像素图上绘制椭圆 下面你可以看到代码 include myimage h include
  • 使用 OpenGL 渲染 QImage

    与我相关的其他问题 https stackoverflow com questions 20126354 render qimage from sooffscreenrenderer in qglwidget 我认为更核心的问题是 如何渲染
  • 如何去除QWizard中的水平线?

    我正在研究一个样式表QWizard我想删除按钮上方的水平线 我尝试递归浏览所有小部件并将其边框设置为无 但似乎没有任何小部件具有此边框 这是我的代码 可以找到完整的可构建示例here https gist github com ardeid
  • Qt 嵌入式触摸屏 QMouseEvents 在收到 MouseButtonRelease 之前未收到

    我在带有触摸屏的小型 ARM 嵌入式 Linux 设备上使用 Qt 4 8 3 我的触摸屏配置了 tslib 并对其进行了校准 因此 etc 中有一个 pointcal 文件 我的触摸事件的位置工作得很好 但无论如何我都会在鼠标按下或鼠标释
  • PyQt4 QPalette 不工作

    btn QtGui QPushButton Button self palettes btn palette palettes setColor btn backgroundRole QtCore Qt green btn setPalet
  • 是否有 Qt 小部件可以浏览应用程序中小部件的层次结构(类似于 Spy++)?

    我们有一个具有复杂的小部件层次结构的应用程序 我希望能够以与 Spy 类似的方式浏览此层次结构 查看和编辑属性 例如大小 如果有一个小部件可以显示此信息 则它不需要在外部应用程序中运行 那么问题来了 这样的神兽存在吗 您可以使用Gammar
  • Qt中用于线程间通信的类设计

    问题陈述 用相机跟踪物体并相应地移动相机的方位角和仰角 Process 相机获取物体的图像 处理相机的每一帧以查找物体 应该被跟踪 并将每帧中生成的信息传递给机械设备 万向节 以平移和倾斜方式移动摄像机 Design 主 Gui 在一个线程
  • Qt 支持 Windows 蓝牙 API 吗?

    谁能告诉我 Qt 是否支持 Windows 蓝牙 API 如果是这样 您能否分享一些有关如何使用它的信息 自上次答复以来 这个问题的答案发生了一些变化 Qt 5 2 版为 Linux BlueZ 和 BlackBerry 设备实现了蓝牙 A
  • 通过单击内部小部件而不是标题栏来移动窗口

    在 Windows 中 当我创建 QMainWindow 时 我可以通过单击标题栏并拖动它来在屏幕上移动它 在我的应用程序中 我使用隐藏了标题栏setWindowFlags Qt CustomizeWindowHint 我正在尝试使用小部件

随机推荐

  • 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 下面就通过示例