/*******************************************************************************************/

一、为什么需要使用线程

图形界面中一旦使用了线程休眠,图形界面就不会刷新(不会动),呈现卡住无响应的状态。

这是由于图形界面中是单线程的

所以  很复杂的数据处理 耗时长的,就需要创建线程。

QThread 线程类,

qt中的线程睡眠函数:QThread::sleep();

void MyWidget::on_pushButton_clicked()

{

//如果定时器没有工作

if(myTimer->isActive() == false)

{

myTimer->start(100);

}

//很复杂的数据处理

//需要耗时5s

sleep(5);//图形界面中一旦使用了线程休眠,图形界面就不会刷新(不会动),呈现卡住无响应的状态。

//也就是说由于睡眠,导致前面启动了的定时器都不工作

myTimer->stop();//过了5s后图形界面才会有响应。但是

//此时是停了定时器,前面是睡眠 定时器也不工作,所以呈现出一直定时器一直不工作的状态

}

/*******************************************************************************************/

二、线程

1.Qt4.7以前 线程的使用方法:

1).自定义一个类,继承于QThread

class MyThread : public QThread

{

public:

void run();//只有这个是线程处理函数(和主线程不在同一个线程),虚函数。

}

2).使用自定义的类创建对象,并开启线程

MyThread myThread;

//启动线程

//注意不能直接调用run函数,否则还是在主线程里面运行,而不是在新线程里面

myThread.start();//间接调用run

3).返回处理结果,告诉处理完

可以在类中定义一个信号,然后在需要(处理好后)的时候发送这个信号,来告诉其他线程。

具体见图《线程1》

2.实现:

在项目中添加一个继承QThread的类,添加的时候注意由于这个不是控件,所以下拉框选择QObject,然后

再在文件中把基类改为QThread

class MyThread : public QThread

{

Q_OBJECT

public:

explicit MyThread(QObject *parent = 0);

protected:

//QThread的虚函数

//线程处理函数

//不能直接调用,通过start()间接调用

void run();

signals:

void isDone();

public slots:

};

void MyThread::run()

{

//很复杂的数据处理

//需要耗时5s

sleep(5);

emit isDone();

}

//分配空间

thread = new MyThread(this);

//处理线程中的信号,最好用这种传统的方式

connect(thread, &MyThread::isDone, this, &MyWidget::dealDone);

//当按窗口右上角x时,窗口触发destroyed(),否则关了窗口线程还会运行

connect(this, &MyWidget::destroyed, this, &MyWidget::stopThread);

void MyWidget::dealDone()

{

qDebug() << "it is over";

myTimer->stop(); //关闭定时器

}

void MyWidget::on_pushButton_clicked()

{

//如果定时器没有工作

if(myTimer->isActive() == false)

{

myTimer->start(100);

}

//启动线程,处理数据

thread->start();

}

void MyWidget::stopThread()

{

//停止线程,不是立马关闭,释放线程占用的内存,线程号等资源。和下面配合使用

thread->quit();//类似linux中,pthread_exit()

//等待线程处理完手头动作

thread->wait();//类似linux中,pthread_join , pthread_detach,

//terminate 强制结束,很暴力,往往会导致内存问题。所以一般不用

}

上述代码具体见《QThread》

  1. #ifndef MYWIDGET_H
  2. #define MYWIDGET_H
  3.  
  4. #include <QWidget>
  5. #include <QTimer> //定时器头文件
  6. #include "mythread.h" //线程头文件
  7.  
  8. namespace Ui {
  9. class MyWidget;
  10. }
  11.  
  12. class MyWidget : public QWidget
  13. {
  14. Q_OBJECT
  15.  
  16. public:
  17. explicit MyWidget(QWidget *parent = );
  18. ~MyWidget();
  19.  
  20. void dealTimeout(); //定时器槽函数
  21. void dealDone(); //线程结束槽函数
  22. void stopThread(); //停止线程槽函数
  23.  
  24. private slots:
  25. void on_pushButton_clicked();
  26.  
  27. private:
  28. Ui::MyWidget *ui;
  29.  
  30. QTimer *myTimer; //声明变量
  31. MyThread *thread; //线程对象
  32. };
  33.  
  34. #endif // MYWIDGET_H

mywidget.h

  1. #include "mywidget.h"
  2. #include "ui_mywidget.h"
  3. #include <QThread>
  4. #include <QDebug>
  5.  
  6. MyWidget::MyWidget(QWidget *parent) :
  7. QWidget(parent),
  8. ui(new Ui::MyWidget)
  9. {
  10. ui->setupUi(this);
  11.  
  12. myTimer = new QTimer(this);
  13.  
  14. //只要定时器启动,自动触发timeout()
  15. connect(myTimer, &QTimer::timeout, this, &MyWidget::dealTimeout);
  16.  
  17. //分配空间
  18. thread = new MyThread(this);
  19.  
  20. connect(thread, &MyThread::isDone, this, &MyWidget::dealDone);
  21.  
  22. //当按窗口右上角x时,窗口触发destroyed()
  23. connect(this, &MyWidget::destroyed, this, &MyWidget::stopThread);
  24.  
  25. }
  26.  
  27. void MyWidget::stopThread()
  28. {
  29. //停止线程
  30. thread->quit();
  31. //等待线程处理完手头动作
  32. thread->wait();
  33. }
  34.  
  35. void MyWidget::dealDone()
  36. {
  37. qDebug() << "it is over";
  38. myTimer->stop(); //关闭定时器
  39. }
  40.  
  41. void MyWidget::dealTimeout()
  42. {
  43. static int i = ;
  44. i++;
  45. //设置lcd的值
  46. ui->lcdNumber->display(i);
  47. }
  48.  
  49. MyWidget::~MyWidget()
  50. {
  51. delete ui;
  52. }
  53.  
  54. void MyWidget::on_pushButton_clicked()
  55. {
  56. //如果定时器没有工作
  57. if(myTimer->isActive() == false)
  58. {
  59. myTimer->start();
  60. }
  61.  
  62. //启动线程,处理数据
  63. thread->start();
  64.  
  65. }

mywidget.cpp

  1. #ifndef MYTHREAD_H
  2. #define MYTHREAD_H
  3.  
  4. #include <QThread>
  5.  
  6. class MyThread : public QThread
  7. {
  8. Q_OBJECT
  9. public:
  10. explicit MyThread(QObject *parent = );
  11.  
  12. protected:
  13. //QThread的虚函数
  14. //线程处理函数
  15. //不能直接调用,通过start()间接调用
  16. void run();
  17.  
  18. signals:
  19. void isDone();
  20.  
  21. public slots:
  22. };
  23.  
  24. #endif // MYTHREAD_H

mythread.h

  1. #include "mythread.h"
  2.  
  3. MyThread::MyThread(QObject *parent) : QThread(parent)
  4. {
  5.  
  6. }
  7.  
  8. void MyThread::run()
  9. {
  10. //很复杂的数据处理
  11. //需要耗时5s
  12. sleep();
  13.  
  14. emit isDone();
  15. }

mythread.cpp

3.Qt4.7以后 线程的使用方法:

1).定义:

(1).设定一个类,继承与QObject

(2).类中设置一个线程函数(只有一个是线程函数,函数名可以任意)

class MyThread : public QObject

{

public:

void fun();//

}

2).使用:

(1).创建自定义线程对象(不能指定父对象),

否则后续就无法加入到QThread线程对象,因为指定后是已经给了窗口了就不能放到线程的地方了

或者说已经有父对象的则不能再移动了(不能移动到别的父对象)

myThread=new MyThread;

(2).创建QThread线程对象

QThread *thread = new QThread(this)

(3).把自定义线程类,加入到QThread线程对象,  关联起来

myThread->moveToThread(thread);

(4).启动线程对象的线程,

thread.start();//只是把线程开启了,并没有启动线程处理函数(线程是启动了,但是线程函数没有启动)

(5).线程处理函数(自定义线程函数)的启动,必须通过signal-slot 信号和槽的方式(不能直接调用这个函数):

主线程发送信号,并且信号的处理函数指定为线程处理函数,这样线程处理函数才启动了

(定义信号与槽必须要有Q_OBJECT宏)

connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout);

(6).线程退出,直接退出是不行的,因为线程处理函数中的while(1)这样退出不了

//当按窗口右上角x时,窗口触发destroyed(),如果此时不关闭线程,则关了窗口线程还会运行

connect(this, &MyWidget::destroyed, this, &MyWidget::dealClose);//不像linux中进程关了则线程都关了,这里线程还会运行

注意使用前面:

thread->quit();//等待线程处理完,但是线程是死循环一致处理不完,所以线程无法退出

thread->wait();

这样线程不退出,必须先让线程处理函数退出循环,通过标志位的方法:

myT->setFlag(true);//设置退出标记,则循环条件不满足,则线程处理函数会退出

thread->quit();

thread->wait()

3).返回处理结果,

可以在类中定义一个信号,信号可以是带参数的,参数可以是处理后的数据

然后在需要(处理好后)的时候发送这个信号,来告诉其他线程。

具体见图《线程2》

4.实现:

void MyThread::myTimeout()

{

while( !isStop )

{

QThread::sleep(1);

emit mySignal();

//QMessageBox::aboutQt(NULL);

qDebug() << "子线程号:" << QThread::currentThread();

if(isStop)

{

break;

}

}

}

void MyWidget::on_buttonStart_clicked()

{

if(thread->isRunning() == true)

{

return;

}

//启动线程,但是没有启动线程处理函数

thread->start();

myT->setFlag(false);

//不能直接调用线程处理函数,

//直接调用,导致,线程处理函数和主线程是在同一个线程

//myT->myTimeout();

//只能通过 signal - slot 方式调用

emit startThread();

}

void MyWidget::on_buttonStop_clicked()

{

if(thread->isRunning() == false)

{

return;

}

myT->setFlag(true);

thread->quit();

thread->wait();

}

5.注意:

1).线程处理函数内部,不允许操作图形界面,也不允许创建图形界面,否则程序会奔溃。

线程处理函数内部一般是纯数据处理

2).connect()第五个参数的作用,第五个参数多线程时才有意义

connect()第五个参数表示的是连接方式,主要有三种:自动连接,队列连接,直接连接

默认的时候,用的是自动连接,自动连接时:

如果是多线程,默认使用队列连接 (通过(接收的)类来判断是否是子线程的)

如果是单线程, 默认使用直接连接方式

队列连接含义: 槽函数所在的线程和接收者一样

connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout);

//槽函数所在的线程和myT一样,即在子线程中

直接连接含义:槽函数所在线程和发送者一样

connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout,Qt::DirectConnection);

//槽函数所在的线程和this一样,即在主线程中,无法实现多任务

这就是为啥,启动线程处理函数使用connect的原因,默认是队列方式(多线程下)。

一般用默认就够了

上述代码具体见《ThreadPro》

  1. #ifndef MYWIDGET_H
  2. #define MYWIDGET_H
  3.  
  4. #include <QWidget>
  5. #include "mythread.h"
  6. #include <QThread>
  7.  
  8. namespace Ui {
  9. class MyWidget;
  10. }
  11.  
  12. class MyWidget : public QWidget
  13. {
  14. Q_OBJECT
  15.  
  16. public:
  17. explicit MyWidget(QWidget *parent = );
  18. ~MyWidget();
  19.  
  20. void dealSignal();
  21. void dealClose();
  22.  
  23. signals:
  24. void startThread(); //启动子线程的信号
  25.  
  26. private slots:
  27. void on_buttonStart_clicked();
  28.  
  29. void on_buttonStop_clicked();
  30.  
  31. private:
  32. Ui::MyWidget *ui;
  33. MyThread *myT;
  34. QThread *thread;
  35.  
  36. };
  37.  
  38. #endif // MYWIDGET_H

mywidget.h

  1. #include "mywidget.h"
  2. #include "ui_mywidget.h"
  3. #include <QDebug>
  4.  
  5. MyWidget::MyWidget(QWidget *parent) :
  6. QWidget(parent),
  7. ui(new Ui::MyWidget)
  8. {
  9. ui->setupUi(this);
  10.  
  11. //动态分配空间,不能指定父对象
  12. myT = new MyThread;
  13.  
  14. //创建子线程
  15. thread = new QThread(this);
  16.  
  17. //把自定义线程加入到子线程中
  18. myT->moveToThread(thread);
  19.  
  20. connect(myT, &MyThread::mySignal, this, &MyWidget::dealSignal);
  21.  
  22. qDebug() << "主线程号:" << QThread::currentThread();
  23.  
  24. connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout);
  25.  
  26. connect(this, &MyWidget::destroyed, this, &MyWidget::dealClose);
  27.  
  28. //线程处理函数内部,不允许操作图形界面
  29.  
  30. //connect()第五个参数的作用,连接方式:默认,队列,直接
  31. //多线程时才有意义
  32. //默认的时候
  33. //如果是多线程,默认使用队列
  34. //如果是单线程, 默认使用直接方式
  35. //队列: 槽函数所在的线程和接收者一样
  36. //直接:槽函数所在线程和发送者一样
  37.  
  38. }
  39.  
  40. MyWidget::~MyWidget()
  41. {
  42. delete ui;
  43. }
  44.  
  45. void MyWidget::dealClose()
  46. {
  47. on_buttonStop_clicked();
  48. delete myT;
  49. }
  50.  
  51. void MyWidget::dealSignal()
  52. {
  53. static int i = ;
  54. i++;
  55. ui->lcdNumber->display(i);
  56. }
  57.  
  58. void MyWidget::on_buttonStart_clicked()
  59. {
  60.  
  61. if(thread->isRunning() == true)
  62. {
  63. return;
  64. }
  65.  
  66. //启动线程,但是没有启动线程处理函数
  67. thread->start();
  68. myT->setFlag(false);
  69.  
  70. //不能直接调用线程处理函数,
  71. //直接调用,导致,线程处理函数和主线程是在同一个线程
  72. //myT->myTimeout();
  73.  
  74. //只能通过 signal - slot 方式调用
  75. emit startThread();
  76.  
  77. }
  78.  
  79. void MyWidget::on_buttonStop_clicked()
  80. {
  81. if(thread->isRunning() == false)
  82. {
  83. return;
  84. }
  85.  
  86. myT->setFlag(true);
  87. thread->quit();
  88. thread->wait();
  89. }

mywidget.cpp

  1. #ifndef MYTHREAD_H
  2. #define MYTHREAD_H
  3.  
  4. #include <QObject>
  5.  
  6. class MyThread : public QObject
  7. {
  8. Q_OBJECT
  9. public:
  10. explicit MyThread(QObject *parent = );
  11.  
  12. //线程处理函数
  13. void myTimeout();
  14.  
  15. void setFlag(bool flag = true);
  16.  
  17. signals:
  18. void mySignal();
  19.  
  20. public slots:
  21.  
  22. private:
  23. bool isStop;
  24. };
  25.  
  26. #endif // MYTHREAD_H

mythread.h

  1. #include "mythread.h"
  2. #include <QThread>
  3. #include <QDebug>
  4. #include <QMessageBox>
  5.  
  6. MyThread::MyThread(QObject *parent) : QObject(parent)
  7. {
  8. isStop = false;
  9. }
  10.  
  11. void MyThread::myTimeout()
  12. {
  13. while( !isStop )
  14. {
  15.  
  16. QThread::sleep();
  17. emit mySignal();
  18. //QMessageBox::aboutQt(NULL);
  19.  
  20. qDebug() << "子线程号:" << QThread::currentThread();
  21.  
  22. if(isStop)
  23. {
  24. break;
  25. }
  26. }
  27. }
  28.  
  29. void MyThread::setFlag(bool flag)
  30. {
  31. isStop = flag;
  32. }

mythread.cpp

两种使用线程的方法,新方式是推荐的,但是老方式更好用。

/*******************************************************************************************/

三、线程画图

线程中可以绘图,可以使用绘图设备QImage

绘画完毕后可以把绘图结果即绘图设备,通过带参数(即绘图设备)的信号发送给主窗口

当绘图很复杂时,当然是放在线程中最合适。

void MyThread::drawImage()

{

//定义QImage绘图设备

QImage image(500, 500, QImage::Format_ARGB32);

//定义画家,指定绘图设备

QPainter p(&image);

//定义画笔对象

QPen pen;

pen.setWidth(5); //设置宽度

//把画笔交给画家

p.setPen(pen);

//定义画刷

QBrush brush;

brush.setStyle(Qt::SolidPattern); //设置样式

brush.setColor(Qt::red); //设置颜色

//把画刷交给画家

p.setBrush(brush);

//定义5个点

QPoint a[] =

{

QPoint(qrand()%500, qrand()%500),

QPoint(qrand()%500, qrand()%500),

QPoint(qrand()%500, qrand()%500),

QPoint(qrand()%500, qrand()%500),

QPoint(qrand()%500, qrand()%500)

};

p.drawPolygon(a, 5);

//通过信号发送图片

emit updateImage(image);

}

上述代码具体见《ThreadIamge》

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3.  
  4. #include <QWidget>
  5. #include "mythread.h"
  6. #include <QThread>
  7. #include <QImage>
  8.  
  9. namespace Ui {
  10. class Widget;
  11. }
  12.  
  13. class Widget : public QWidget
  14. {
  15. Q_OBJECT
  16.  
  17. public:
  18. explicit Widget(QWidget *parent = );
  19. ~Widget();
  20.  
  21. //重写绘图事件
  22. void paintEvent(QPaintEvent *);
  23.  
  24. void getImage(QImage); //槽函数
  25. void dealClose(); //窗口关闭槽函数
  26.  
  27. private:
  28. Ui::Widget *ui;
  29. QImage image;
  30. MyThread *myT; //自定义线程对象
  31. QThread *thread; //子线程
  32. };
  33.  
  34. #endif // WIDGET_H

widget.h

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include <QPainter>
  4. #include <QThread>
  5.  
  6. Widget::Widget(QWidget *parent) :
  7. QWidget(parent),
  8. ui(new Ui::Widget)
  9. {
  10. ui->setupUi(this);
  11.  
  12. //自定义类对象,分配空间,不可以指定父对象
  13. myT = new MyThread;
  14.  
  15. //创建子线程
  16. thread = new QThread(this);
  17.  
  18. //把自定义模块添加到子线程
  19. myT->moveToThread(thread);
  20.  
  21. //启动子线程,但是,并没有启动线程处理函数
  22. thread->start();
  23.  
  24. //线程处理函数,必须通过signal - slot 调用
  25. connect(ui->pushButton, &QPushButton::pressed, myT, &MyThread::drawImage);
  26. connect(myT, &MyThread::updateImage, this, &Widget::getImage);
  27.  
  28. connect(this, &Widget::destroyed, this, &Widget::dealClose);
  29.  
  30. }
  31.  
  32. Widget::~Widget()
  33. {
  34. delete ui;
  35. }
  36.  
  37. void Widget::dealClose()
  38. {
  39. //退出子线程
  40. thread->quit();
  41. //回收资源
  42. thread->wait();
  43. delete myT;
  44.  
  45. }
  46.  
  47. void Widget::getImage(QImage temp)
  48. {
  49. image = temp;
  50. update(); //更新窗口,间接调用paintEvent()
  51. }
  52.  
  53. void Widget::paintEvent(QPaintEvent *)
  54. {
  55. QPainter p(this); //创建画家,指定绘图设备为窗口
  56. p.drawImage(, , image);
  57. }

widget.cpp

  1. #ifndef MYTHREAD_H
  2. #define MYTHREAD_H
  3.  
  4. #include <QObject>
  5. #include <QImage>
  6.  
  7. class MyThread : public QObject
  8. {
  9. Q_OBJECT
  10. public:
  11. explicit MyThread(QObject *parent = );
  12.  
  13. //线程处理函数
  14. void drawImage();
  15.  
  16. signals:
  17. void updateImage(QImage temp);
  18.  
  19. public slots:
  20. };
  21.  
  22. #endif // MYTHREAD_H

mythread.h

  1. #include "mythread.h"
  2. #include <QPainter>
  3. #include <QPen>
  4. #include <QBrush>
  5. #include <QImage>
  6.  
  7. MyThread::MyThread(QObject *parent) : QObject(parent)
  8. {
  9.  
  10. }
  11.  
  12. void MyThread::drawImage()
  13. {
  14. //定义QImage绘图设备
  15. QImage image(, , QImage::Format_ARGB32);
  16. //定义画家,指定绘图设备
  17. QPainter p(&image);
  18.  
  19. //定义画笔对象
  20. QPen pen;
  21. pen.setWidth(); //设置宽度
  22. //把画笔交给画家
  23. p.setPen(pen);
  24.  
  25. //定义画刷
  26. QBrush brush;
  27. brush.setStyle(Qt::SolidPattern); //设置样式
  28. brush.setColor(Qt::red); //设置颜色
  29. //把画刷交给画家
  30. p.setBrush(brush);
  31.  
  32. //定义5个点
  33. QPoint a[] =
  34. {
  35. QPoint(qrand()%, qrand()%),
  36. QPoint(qrand()%, qrand()%),
  37. QPoint(qrand()%, qrand()%),
  38. QPoint(qrand()%, qrand()%),
  39. QPoint(qrand()%, qrand()%)
  40. };
  41.  
  42. p.drawPolygon(a, );
  43.  
  44. //通过信号发送图片
  45. emit updateImage(image);
  46.  
  47. }

mythread.cpp

界面编程之QT的线程20180731的更多相关文章

  1. 界面编程之QT的Socket通信20180730

    /*******************************************************************************************/ 一.linu ...

  2. 界面编程之QT的数据库操作20180801

    /*******************************************************************************************/ 一.数据库连 ...

  3. 界面编程之QT绘图和绘图设备20180728

    /*******************************************************************************************/ 一.绘图 整 ...

  4. 界面编程之QT的事件20180727

    /*******************************************************************************************/ 一.事件 1 ...

  5. 界面编程之QT窗口系统20180726

    /*******************************************************************************************/ 一.坐标系统 ...

  6. 界面编程之QT的信号与槽20180725

    /*******************************************************************************************/ 一.指定父对 ...

  7. 界面编程之QT的基本介绍与使用20180722

    /*******************************************************************************************/ 一.qt介绍 ...

  8. 界面编程之QT的文件操作20180729

    /*******************************************************************************************/ 一.QT文件 ...

  9. QT核心编程之Qt线程 (c)

    QT核心编程之Qt线程是本节要介绍的内容,QT核心编程我们要分几个部分来介绍,想参考更多内容,请看末尾的编辑推荐进行详细阅读,先来看本篇内容. Qt对线程提供了支持,它引入了一些基本与平台无关的线程类 ...

随机推荐

  1. Exp8 web基础

    20155332<网络对抗>Exp5 MSF基础应用 1.实验环境搭建 1.apache的安装与配置 安装:sudo apt-get install apache2 开启:service ...

  2. proftpd启动失败提示unable to determine IP address of “xxx.com”

    proftpd启动失败提示unable to determine IP address of “xxx.com”这种proftpd启动失败的原因是无法解析后面主机的IP地址,解决方法是在DNS列表中增 ...

  3. Linux中tty、pty、pts的概念区别 转载

    基本概念: > tty(终端设备的统称): tty一词源于Teletypes,或teletypewriters,原来指的是电传打字机,是通过串行线用打印机键盘通过阅读和发送信息的东西,后来这东西 ...

  4. git和github使用教程

    看官请移步git和github简单教程, 本文是上述链接的截图,担心哪天作者不小心删除了,备一份在自己这里,仅为自己看着方便.侵权请告知

  5. SpringBoot日记——信息修改PUT篇

    我们常用的功能,除了post和get,还有put和delete,这篇文章就介绍一下这个put的基本用法. 页面跳转和回显 1. 首先,我们之前的页面已经将添加和修改的按钮都做好了,那么如何实现这些按钮 ...

  6. VMware在Centos7上配置静态IP的方法

    使用NAT模式 在这里记下192.168.161.2 进入系统,为系统自动分配一个ip 记录下 192.168.161.129 进入网络管理器配置文件目录 cd /etc/sysconfig/netw ...

  7. leetcode-递增的三元子序列

    给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列. 数学表达式如下: 如果存在这样的 i, j, k,  且满足 0 ≤ i < j < k ≤ n-1,使得 arr[ ...

  8. Hyperldeger Fabric踩过的坑

    给参与者颁发身份时错误 错误信息: fabric-ca request register failed with errors [[{"code":400,"messag ...

  9. ping命令使用及其常用参数

    PING (Packet Internet Groper),因特网包探索器,用于测试网络连接量检查网络是否连通,可以很好地帮助我们分析和判定网络故障.Ping发送一个ICMP(Internet Con ...

  10. Intellij IDEA 文件修改提示星号

    https://www.cnblogs.com/zheting/p/7594073.html