• 操作系统 线程的创建


    首先了解一下进程和线程
    
    进程:执行任务的独立集合  分配资源的基本单位
    线程:隶属于进程的一部分  分配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():  我们想要的功能  要在线程函数里实现

    先展示主界面包含什么

    1. #ifndef WIDGET_H
    2. #define WIDGET_H
    3. #include
    4. #include
    5. #include
    6. QT_BEGIN_NAMESPACE
    7. namespace Ui { class Widget; }
    8. QT_END_NAMESPACE
    9. class Widget : public QWidget
    10. {
    11. Q_OBJECT
    12. public:
    13. Widget(QWidget *parent = nullptr);
    14. ~Widget();
    15. signals:
    16. void signalSetValue(int );//线程函数里发射的信号 用于修改进度条
    17. public slots:
    18. void slotSetValue(int );//线程函数里发射的信号的槽函数 修改进度条
    19. private slots:
    20. void on_pushButton_clicked(); //go按钮槽函数
    21. void on_pushButton_2_clicked();//pause按钮槽函数
    22. void on_pushButton_3_clicked();//stop按钮槽函数
    23. private:
    24. Ui::Widget *ui;
    25. public:
    26. HANDLE hTherad;//用于记录线程的句柄
    27. bool Mark;//stop按钮用到 用于控制线程的结束
    28. };
    29. #endif // WIDGET_H

    我们首先为按钮go添加一个槽函数

    1. void Widget::on_pushButton_clicked()
    2. {
    3. //创建线程
    4. if(! hTherad)
    5. {
    6. Mark=true;
    7. hTherad= CreateThread(
    8. 0,//线程安全属性
    9. 0,//线程栈的大小 默认1MB
    10. &TheradProc,//线程函数
    11. this,//线程参数
    12. 0,//创建标志 0 立刻运行 CREATE_SUSPENDED 挂起
    13. 0//线程id
    14. );
    15. }
    16. else
    17. {
    18. ResumeThread(hTherad);
    19. //恢复线程
    20. //SuspendThread(hTherad); 挂起线程 1
    21. //SuspendThread(hTherad); 挂起线程 2
    22. //ResumeThread(hTherad); 恢复线程 1
    23. //ResumeThread(hTherad); 恢复线程 0
    24. }
    25. }

    在槽函数中我们添加一个线程

    同时将我们的线程的句柄赋给hTherad 

    之后我们就可以通过hTherad对线程进行一些操作了

    将创建线程的函数: CreateThread的参数 :全局线程函数TheradProc()定义出来

    在TheradProc函数中 我们要对进度条进行操作 

    在全局函数中不识别当前的界面ui  不能直接对界面上的空间内进行操作

    可以通过连接信号和槽进行间接操作

    同时 我们又对其加了一个判断条件  如果线程已经被创建出来  就无须再进行创建了

    只需要让进度条动起来就行

     else
        {
            ResumeThread(hTherad);
            //恢复线程

            //SuspendThread(hTherad);  挂起线程 1
            //SuspendThread(hTherad);  挂起线程 2
            //ResumeThread(hTherad);   恢复线程 1
            //ResumeThread(hTherad);   恢复线程 0

        }

    这里演示了挂起线程和恢复线程

    如:挂起n次线程  需要恢复n次线程才能让线程动起来  

    1. DWORD WINAPI TheradProc(LPVOID lpParameter)
    2. //线程函数是一个全局函数 不能识别ui 所以线程参数 定义为Widget*类型
    3. {
    4. Widget*wig=(Widget*)lpParameter;
    5. for (int i=0;i<=100&&wig->Mark==true;i++) {
    6. //发送信号 传i的值用于修改
    7. emit wig->signalSetValue(i);
    8. Sleep(100);//每隔100毫秒执行一次
    9. }
    10. return 0;
    11. }

    槽函数

    1. void Widget::slotSetValue(int i)
    2. {
    3. //对进度条修改
    4. ui->progressBar->setValue(i);
    5. }

    在Widget界面的构造函数进行信号的连接

     connect(this,&Widget::signalSetValue,this,&Widget::slotSetValue);
    到这里我们的go按钮就完成了

    接下来开始编写我们的pause按钮的功能

    对pause按钮添加一个槽函数

    1. void Widget::on_pushButton_2_clicked()
    2. {
    3. SuspendThread(hTherad);
    4. //挂起线程
    5. }

    只需要将线程挂起就可以达到暂停的作用

    接下来 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;
        }

    一定会使进程结束

    缺点:当进程在开辟空间的过程中   突然被杀死  那么开辟的空间不会被回收  并且空间的锁无法访问  造成内存泄露

    1. void Widget::on_pushButton_3_clicked()
    2. {
    3. //正常退出
    4. Mark=false;
    5. //强制杀死
    6. //TerminateThread(hTherad,-1);
    7. //3.能正常退出 则正常退出 如果不能 则强制杀死
    8. //(线程正常退出时会发送一个信号 如果信号=WAIT_TIMEOUT则视为正常退出)
    9. if(hTherad)
    10. {
    11. if(WAIT_TIMEOUT==WaitForSingleObject(hTherad,100))
    12. TerminateThread(hTherad,-1);
    13. CloseHandle(hTherad);
    14. hTherad=NULL;
    15. }
    16. ui->progressBar->setValue(0);
    17. }

    3.结合的方式结束(一般我们用)

    //正常退出
        Mark=false;
       
        //(线程正常退出时会发送一个信号  如果信号=WAIT_TIMEOUT则视为正常退出)
        if(hTherad)
        {
            if(WAIT_TIMEOUT==WaitForSingleObject(hTherad,100))
                    TerminateThread(hTherad,-1);

            CloseHandle(hTherad);
            hTherad=NULL;
        }
        ui->progressBar->setValue(0);

    完整代码如下

  • 相关阅读:
    含电热联合系统的微电网运行优化附Matlab代码
    使用开源软件Inno Setup制作软件安装包
    Pytorch 中 Dataset 和 DataLoader,以及 torchvision 的 datasets 完全理解
    数据库与缓存更新一致性四种策略
    反汇编ARM程序的技术靠谱吗?——揭秘ARM架构二进制程序的反汇编技术现状
    雷达基础导论及MATLAB仿真
    位运算符在SQL中的使用场景
    User-Agent配置问题导致手机端展示异常但是safari展示正常
    [ vulhub漏洞复现篇 ] Hadoop-yarn-RPC 未授权访问漏洞复现
    【dbeaver】win环境的kerberos认证和Clouders集群中Kerberos认证使用Dbeaver连接Hive和Phoenix
  • 原文地址:https://blog.csdn.net/van9527/article/details/126155344