首先了解一下进程和线程 进程:执行任务的独立集合 分配资源的基本单位 线程:隶属于进程的一部分 分配cpu的基本单位(轮片时间片的基本单位) 线程的运行方式: 1.并发 多个执行命令交替执行 2.并行 多个执行命令同时执行 例如 : a=0; 两个线程执行 a++ 并发执行:a=2 并行执行:a=1 线程的组成部分: 1.线程栈(堆栈区):用来存放线程的临时的 局部的资源 2.内核对象:计数器(2) (句柄关闭-1,线程退出-1) 挂起计数器 信号(无):如果线程退出 会变成有信号 ;不退出 无信号 线程的挂起和恢复: 由挂起计数器控制 计数为0时才真正的执行挂起和恢复 我们想实现三个按钮控制进度条的界面

这里我们想要实现
按下go按钮 进度条增长
按下pause按钮 进度条暂停
按下stop按钮 进度条清空 杀死进程
创建线程的函数: CreateThread(0,//线程安全属性
0,//线程栈的大小 默认1MB
&TheradProc,//线程函数
this,//线程参数
0,//创建标志 0 立刻运行 CREATE_SUSPENDED 挂起
0//线程id
);
其中 线程函数TheradProc(): 我们想要的功能 要在线程函数里实现
先展示主界面包含什么
- #ifndef WIDGET_H
- #define WIDGET_H
- #include
- #include
- #include
- QT_BEGIN_NAMESPACE
- namespace Ui { class Widget; }
- QT_END_NAMESPACE
-
- class Widget : public QWidget
- {
- Q_OBJECT
-
- public:
- Widget(QWidget *parent = nullptr);
- ~Widget();
-
-
- signals:
- void signalSetValue(int );//线程函数里发射的信号 用于修改进度条
- public slots:
- void slotSetValue(int );//线程函数里发射的信号的槽函数 修改进度条
- private slots:
- void on_pushButton_clicked(); //go按钮槽函数
-
- void on_pushButton_2_clicked();//pause按钮槽函数
-
- void on_pushButton_3_clicked();//stop按钮槽函数
-
- private:
-
- Ui::Widget *ui;
- public:
- HANDLE hTherad;//用于记录线程的句柄
- bool Mark;//stop按钮用到 用于控制线程的结束
- };
- #endif // WIDGET_H
我们首先为按钮go添加一个槽函数
- void Widget::on_pushButton_clicked()
- {
-
- //创建线程
- if(! hTherad)
- {
- Mark=true;
- hTherad= CreateThread(
- 0,//线程安全属性
- 0,//线程栈的大小 默认1MB
- &TheradProc,//线程函数
- this,//线程参数
- 0,//创建标志 0 立刻运行 CREATE_SUSPENDED 挂起
- 0//线程id
- );
- }
- else
- {
- ResumeThread(hTherad);
- //恢复线程
-
- //SuspendThread(hTherad); 挂起线程 1
- //SuspendThread(hTherad); 挂起线程 2
- //ResumeThread(hTherad); 恢复线程 1
- //ResumeThread(hTherad); 恢复线程 0
-
- }
-
-
- }
在槽函数中我们添加一个线程
同时将我们的线程的句柄赋给hTherad
之后我们就可以通过hTherad对线程进行一些操作了
将创建线程的函数: CreateThread的参数 :全局线程函数TheradProc()定义出来
在TheradProc函数中 我们要对进度条进行操作
在全局函数中不识别当前的界面ui 不能直接对界面上的空间内进行操作
可以通过连接信号和槽进行间接操作
同时 我们又对其加了一个判断条件 如果线程已经被创建出来 就无须再进行创建了
只需要让进度条动起来就行
else
{
ResumeThread(hTherad);
//恢复线程
//SuspendThread(hTherad); 挂起线程 1
//SuspendThread(hTherad); 挂起线程 2
//ResumeThread(hTherad); 恢复线程 1
//ResumeThread(hTherad); 恢复线程 0
}
这里演示了挂起线程和恢复线程
如:挂起n次线程 需要恢复n次线程才能让线程动起来
- DWORD WINAPI TheradProc(LPVOID lpParameter)
- //线程函数是一个全局函数 不能识别ui 所以线程参数 定义为Widget*类型
- {
- Widget*wig=(Widget*)lpParameter;
-
-
- for (int i=0;i<=100&&wig->Mark==true;i++) {
-
- //发送信号 传i的值用于修改
- emit wig->signalSetValue(i);
- Sleep(100);//每隔100毫秒执行一次
- }
-
-
- return 0;
- }
槽函数
- void Widget::slotSetValue(int i)
- {
-
- //对进度条修改
- ui->progressBar->setValue(i);
-
-
- }
在Widget界面的构造函数进行信号的连接
connect(this,&Widget::signalSetValue,this,&Widget::slotSetValue);
到这里我们的go按钮就完成了
接下来开始编写我们的pause按钮的功能
对pause按钮添加一个槽函数
- void Widget::on_pushButton_2_clicked()
- {
- SuspendThread(hTherad);
- //挂起线程
- }
只需要将线程挂起就可以达到暂停的作用
接下来 stop按钮
这里我们有三种方法来结束线程
1.正常结束
Mark=false;
if(hTherad)
{
CloseHandle(hTherad);
hTherad=NULL;
}
ui->progressBar->setValue(0);
标记赋值为false
如果句柄不等于空 就把句柄回收
缺点 :无法确保线程一定会结束 只是把句柄回收了
2.强制杀死
Mark=false;
if(hTherad)
{
TerminateThread(hTherad,-1);
CloseHandle(hTherad);
hTherad=NULL;
}
一定会使进程结束
缺点:当进程在开辟空间的过程中 突然被杀死 那么开辟的空间不会被回收 并且空间的锁无法访问 造成内存泄露
- void Widget::on_pushButton_3_clicked()
- {
-
- //正常退出
- Mark=false;
- //强制杀死
- //TerminateThread(hTherad,-1);
- //3.能正常退出 则正常退出 如果不能 则强制杀死
- //(线程正常退出时会发送一个信号 如果信号=WAIT_TIMEOUT则视为正常退出)
- if(hTherad)
- {
- if(WAIT_TIMEOUT==WaitForSingleObject(hTherad,100))
- TerminateThread(hTherad,-1);
-
- CloseHandle(hTherad);
- hTherad=NULL;
- }
- ui->progressBar->setValue(0);
-
- }
3.结合的方式结束(一般我们用)
//正常退出
Mark=false;
//(线程正常退出时会发送一个信号 如果信号=WAIT_TIMEOUT则视为正常退出)
if(hTherad)
{
if(WAIT_TIMEOUT==WaitForSingleObject(hTherad,100))
TerminateThread(hTherad,-1);
CloseHandle(hTherad);
hTherad=NULL;
}
ui->progressBar->setValue(0);
完整代码如下