【C++】 Qt-线程挂起、恢复和退出

线程挂起和恢复

我们给设置线程的函数创建一个线程句柄用来接收返回值,并且将状态改为挂起状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cgqxSAYx-1689132952536)(C++.assets/image-20230704060636417.png)]

然后在循环中当第五秒时恢复线程,第八秒时连续挂起两次线程,并且返回输出挂起计数器的值(返回挂起之前的),最后在第10秒和第12秒分别恢复一次线程,同样返回 挂起计数器,恢复之前的数值

    for(int i=0;i<20;i++){
    
    
        qDebug()<<"-------------------睡觉:"<<i;

        if(i == 5){
    
    
            //恢复线程
            ::ResumeThread(handle);
        }
        if(i == 8){
    
    
            //挂起之前的:挂起计数器的值
            DWORD d1 = ::SuspendThread(handle);  //挂起
            qDebug()<<"d1 = "<<d1;  //0

            DWORD d2 = ::SuspendThread(handle);  //挂起
            qDebug()<<"d2 = "<<d2;  //1
        }
        if(i == 10){
    
    
            //恢复线程,返回的是挂起计数器,恢复之前的数值
            DWORD d3 = ::ResumeThread(handle);  //恢复
            qDebug()<<"d3 = "<<d3;  //2
        }
        if(i == 12){
    
    
            DWORD d4 = ::ResumeThread(handle);
            qDebug()<<"d4 = "<<d4;  //1
            
            //挂起几次,就要恢复几次,挂起计数器的值为0,线程开始执行
        }
        Sleep(1000);
    }

通过测试我们发现,挂起几次就要 恢复几次,挂起计数器 的值为0时,线程开始执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9gQ82Qyl-1689132952537)(C++.assets/image-20230704061101314.png)]

内核对象

内核是操作系统提供底层服务的一个模块,而内核对象则是内核分配的一个内存块,它是一种数据结构,不同的内核对象具有不同的结构,负责维护对象相关的信息 ,少数成员如:安全描述符、使用计数等是所有内核对象都有的,其他多数都是不同的。

内核对象的所有者是操作系统而非进程,即内核对象的生命周期并不一定会随着创建该对象的进程的消亡而消亡,内核对象的存在时间可以比创建该对象的进程长,内核对象的回收是通过使用计数来实现的。使用计数是所有内核对象固有的属性,操纵系统通过使用计数维护内核对象的生命周期。

操作系统内核知道当前有多少进程正在使用某个内核对象,内核对象被创建时,其使用计数为1,另一个进程访问该内核对象后,使用计数加1,当进程终止时,使用计数减1,手动关闭内核对象时,使用计数再减1,最终使用计数为 0时,操作系统将销毁该内核对象。

如果结束使用内核对象,需要调用CloseHandle()函数,BOOL CloseHandle(HANDLE hObject);

注意:并不是说调用这个函数,内核对象就被销毁了,而是使使用计数器减1.如果忘记CloseHandle那么在程序运行期间会发生内存泄漏,直到进程结束,操作系统会回收所有的资源。

线程退出

  1. 正常退出,如顺序执行完代码,或有限次的循环,执行完毕直接自己 主动退出。退出时记得CloseHandle()。
    if(handle){
    
    
        ::CloseHandle(handle);  //使用计数减1
        handle = nullptr;
    }
  1. 全局变量(双变量标记)

有些时候,创建的线程无法正常退出,可以设置退出标志,最好设置两个标志,可以让双方友好和谐的退出。保证所有子线程都退出后,主线程再退出。

bool isQuitThread = false;  //不退出

bool isQuit = false;  //不退出
    while(!isQuitThread){
    
    
        qDebug()<<"挣钱----";
        Sleep(1000);
    }
    qDebug()<<"我即将要退出了";
    isQuit = true;  //我即将要退出了,告诉主线程一声
    while(1){
    
    
        if(isQuit){
    
    
            qDebug()<<"等到了子线程退出";
            break;
        }
        Sleep(1000);
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tJmzlc7E-1689132952538)(C++.assets/image-20230704070532463.png)]

  1. 终止线程

上例中如果子线程因为某种原因,一直无法退出,将导致主线程一直等待,无法做其他的事情。所以等待应该是有时限的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gtP73Gg0-1689132952538)(C++.assets/image-20230704070735326.png)]

    //等待子线程退出,等待多少毫秒,阻塞函数
    DWORD flag = ::WaitForSingleObject(handle,3000);
    if(flag == WAIT_TIMEOUT){
    
      //等待超时
        qDebug()<<"等待超时,强制杀死";
        //强制杀死子线程
        ::TerminateThread(handle,-1);  //最后万不得已的手段,有风险
    }else if(flag == WAIT_OBJECT_0){
    
      //正常等到子线程退出了
        qDebug()<<"等到了子线程退出";
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8imG6lxZ-1689132952538)(C++.assets/image-20230704070848878.png)]

注意:TerminateThread 是一个比较危险的方法,应当用在最后万不得已最极端的情况,如果目标线程正在使用关键段,关键段不会被释放。线程在堆区申请空间,不会被释放,可能会导致内存泄漏。

猜你喜欢

转载自blog.csdn.net/jia_03/article/details/131678101