当前位置: 首页 > article >正文

虎先锋,你也喜欢线程控制嘛

讲讲线程控制捏

线程创建

这是创建线程调用的接口:

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

这个接口上一篇文章已经介绍过了

线程等待

那么我们来看看下一个等待的接口:

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

 第一个参数是你要等哪个线程,就把哪个线程的ID输进去,第二个参数设为nullptr就好(不关心它的返回值的话)

然后就是写一段代码:

#include <iostream>
#include<string>
#include<pthread.h>
#include <unistd.h>
#include <ctime>

void* threadRun(void* args)
{

}

int main()
{
    pthread_t tid;      //线程的ID
    int n = pthread_create(&tid,nullptr,threadRun,(void*)"thread 1");
    if(n != 0)
    {
        std::cerr << "create thread error" << std::endl;
        return 1;
    }
    return 0;
}

问题来了:main和new线程哪个先运行?

答案是:不确定。 

和进程那套一样,就是不确定

第二个问题:我们期望谁最后退出?

在进程阶段,我们希望父进程后退出

因为我们希望父进程给紫禁城擦屁股

同样的,放到线程这里,我们希望main thread能提前退出

所以在我们的代码里放上线程等待的部分:

#include <iostream>
#include<string>
#include<pthread.h>
#include <unistd.h>
#include <ctime>

void* threadRun(void* args)
{

}

int main()
{
    pthread_t tid;      //线程的ID
    int n = pthread_create(&tid,nullptr,threadRun,(void*)"thread 1");
    if(n != 0)
    {
        std::cerr << "create thread error" << std::endl;
        return 1;
    }
    n = pthread_join(tid,nullptr);
    if(n == 0)
    {
        std::cout << "main thread wait success" << std::endl;
    }
    return 0;
}

如果不join,那么主线程退出,新线程也就跟着退掉了,容易造成僵尸进程的问题

我们来验证一下这个join:

#include <iostream>
#include<string>
#include<pthread.h>
#include <unistd.h>
#include <ctime>

void* threadRun(void* args)
{
    int cnt = 5;
    while (cnt)
    {
        std::cout << "new thread run ...,cnt : " << cnt-- << std::endl;
        sleep(1);
    }
    return nullptr;    
}

int main()
{
    pthread_t tid;      //线程的ID
    int n = pthread_create(&tid,nullptr,threadRun,(void*)"thread 1");
    if(n != 0)
    {
        std::cerr << "create thread error" << std::endl;
        return 1;
    }
    n = pthread_join(tid,nullptr);
    std::cout << "main thread join begin..." << std::endl;
    if(n == 0)
    {
        std::cout << "main thread wait success" << std::endl;
    }
    return 0;
}
while :; do ps -aL;sleep 1;done

 这个线程id是一个虚拟地址(tid)

那么我们怎么看待线程传参的行为呢?

我们可以传递各种类型的参数,但是自己要记得传了啥(也可以传递类对象的地址):

#include <iostream>
#include<string>
#include<pthread.h>
#include <unistd.h>
#include <ctime>

class ThreadData
{
public:
    std::string name;
    std::string num;
};

void* threadRun(void* args)
{
    int cnt = 5;
    ThreadData *td = static_cast<ThreadData*>(args);
    while (cnt)
    {
        std::cout << "new thread run ...,cnt : " << cnt-- << std::endl;
        sleep(1);
    }
    return nullptr;    
}

int main()
{
    pthread_t tid;      //线程的ID
    ThreadData td;
    td.name = "thread-1";
    td.num = 1;
    int n = pthread_create(&tid,nullptr,threadRun,(void*)&td);
    if(n != 0)
    {
        std::cerr << "create thread error" << std::endl;
        return 1;
    }
    n = pthread_join(tid,nullptr);
    std::cout << "main thread join begin..." << std::endl;
    if(n == 0)
    {
        std::cout << "main thread wait success" << std::endl;
    }
    return 0;
}

static_cast是安全类别的强转,对目标对象会做安装性检查

我们刚才是让新线程访问主线程栈上的变量,它有点破坏主线程的完整性和独立性了,而且如果要是两个线程同时访问,更容易出问题

我们建议是申请一块空间:

#include <iostream>
#include<string>
#include<pthread.h>
#include <unistd.h>
#include <ctime>

class ThreadData
{
public:
    std::string name;
    std::string num;
};

void* threadRun(void* args)
{
    int cnt = 5;
    ThreadData *td = static_cast<ThreadData*>(args);
    while (cnt)
    {
        std::cout << "new thread run ...,cnt : " << cnt-- << std::endl;
        sleep(1);
    }
    return nullptr;    
}

int main()
{
    pthread_t tid;      //线程的ID
    ThreadData *td = new ThreadData();
    td->name = "thread-1";
    td->num = 1;
    int n = pthread_create(&tid,nullptr,threadRun,td);
    if(n != 0)
    {
        std::cerr << "create thread error" << std::endl;
        return 1;
    }
    n = pthread_join(tid,nullptr);
    std::cout << "main thread join begin..." << std::endl;
    if(n == 0)
    {
        std::cout << "main thread wait success" << std::endl;
    }
    return 0;
}

人手一个堆空间,多线程就不会相互干扰了

邮专,你让我们人手一个上床下桌,光电孙和计科爷就不会相互干扰了

柚专,你让我们人手一个通行码,首陀罗和婆罗门就不会相互干扰了

传参问题搞定了,是时候来看看返回值是怎么会事了

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

这个retval是一个输出型参数

 因为线程退出时的返回值是void*,所以想要获取新线程退出时的返回值就需要传void**

void *code = nullptr;           //开辟了空间
n = pthread_join(tid,&code);

这样就可以获取新线程的退出信息,通过退出信息可以得知线程的任务完成情况

那么我们应该怎么看待线程的函数返回呢?

事实证明,我们只需要考虑正确的返回,不考虑异常,因为异常了的话整个程序就崩溃了,包括主线程

我们不仅可以返回数字,还可以返回结构体对象

但是我们截至目前还只是创建了一个线程,怎样创建多线程呢?

因为线程创建的时候需要有自己的ID,所以我们如果想要优雅的创建则需要用到vector

#include <iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include <unistd.h>
#include <ctime>

void* threadrun(void* args)
{
    std::string name = static_cast<const char*>(args);
    while (true)
    {
        std::cout << name << "is running" << std::endl;
        sleep(1);
    }
    
}

const int num = 10;

int main()
{
    std::vector<pthread_t>tids;
    for(int i=0;i<num;i++)
    {
        //有线程的ID
        pthread_t tid;
        char name[128];
        snprintf(name,sizeof(name),"thread-%d",i+1);

        pthread_create(&tid,nullptr,threadrun,name);
    }
    sleep(100);
    return 0;
}

但是这个代码很奇怪

 

因为线程都去执行这个, 线程创建是那样创建的,但是谁先调度是不确定的

而因为这个name是在主线程的栈空间上的,所以这么多线程都去访问就容易出现数据问题

所以我们需要改变一下我们的code:

#include <iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include <unistd.h>
#include <ctime>

void* threadrun(void* args)
{
    std::string name = static_cast<const char*>(args);
    while (true)
    {
        std::cout << name << "is running" << std::endl;
        sleep(1);
    }
    
}

const int num = 10;

int main()
{
    std::vector<pthread_t>tids;
    for(int i=0;i<num;i++)
    {
        //有线程的ID
        pthread_t tid;
        char *name = new char[128];
        snprintf(name,128,"thread-%d",i+1);

        pthread_create(&tid,nullptr,threadrun,name);
    }
    sleep(100);
    return 0;
}

 为了方便管理,我们要保存所有的线程的ID信息

#include <iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include <unistd.h>
#include <ctime>

void* threadrun(void* args)
{
    std::string name = static_cast<const char*>(args);
    while (true)
    {
        std::cout << name << "is running" << std::endl;
        sleep(1);
        break;
    }
    return nullptr;
}

const int num = 10;

int main()
{
    std::vector<pthread_t>tids;
    for(int i=0;i<num;i++)
    {
        //有线程的ID
        pthread_t tid;
        char *name = new char[128];
        snprintf(name,128,"thread-%d",i+1);

        pthread_create(&tid,nullptr,threadrun,name);
        tids.emplace_back(tid);
    }

    for(auto tid : tids)
    {
        pthread_join(tid,nullptr);
    }
    return 0;
}

如果加上线程的等待的话就是:

#include <iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include <unistd.h>
#include <ctime>

void* threadrun(void* args)
{
    std::string name = static_cast<const char*>(args);
    while (true)
    {
        std::cout << name << " is running" << std::endl;
        sleep(1);
        break;
    }
    return args;
}

const int num = 10;

int main()
{
    std::vector<pthread_t>tids;
    for(int i=0;i<num;i++)
    {
        //有线程的ID
        pthread_t tid;
        char *name = new char[128];
        snprintf(name,128,"thread-%d",i+1);

        pthread_create(&tid,nullptr,threadrun,name);
        tids.push_back(tid);
    }

    for(auto tid : tids)
    {
        void *name = nullptr;
        pthread_join(tid,&name);
        std::cout << (const char*)name << " quit... " << std::endl;
        delete (const char*)name;
    }
    return 0;
}

 

线程终止

接下来来看看线程如何终止吧

如果main函数结束,那么也代表着main thread结束,而mainthread结束也代表着进程结束了

所以我们得保证主线程退出的时候其他线程差不多跑完了

return和exit有区别,调用exit表示进程终止,而return可以用来终止线程

而想要线程结束可以调用一个接口:

#include <pthread.h>
void pthread_exit(void *retval);

这是控制线程退出 

void* threadrun(void* args)
{
    std::string name = static_cast<const char*>(args);
    while (true)
    {
        std::cout << name << " is running" << std::endl;
        sleep(1);
        break;
    }
    pthread_exit(args);
}

#include <pthread.h>
int pthread_cancel(pthread_t thread);
Compile and link with -pthread.

取消线程需要一个条件:线程存在(不存在取消集贸啊)

线程被取消之后的退出结果是-1

线程分离

 我们可不可以不join线程,让它自己执行完之后就退出啊?

答案肯定是可以的,那么我们应该怎么做呢?

引入一个函数:

#include <pthread.h>
int pthread_detach(pthread_t thread);

炫酷的,,,线程分离!!!

当一个线程被创建,那么它默认是joinable的,是必须要join的

如果一个线程被分离,那么它不能也不需要被join

 线程可以自己分离自己,也可以主线程分离新线程,只要线程存在即可

分离之后的线程相当于跟所有人断绝关系,自生自灭

C嘎嘎也有线程库,那我为什么还要用里牛渴死的原生线程库?

学长:这不叫里牛渴死

 

#include <iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include<thread>
#include<stdlib.h>
#include <unistd.h>

void threadRun(std::string name,int num)
{
    while (num)
    {
        std::cout << name << " num: " << num << std::endl;
        num--;
        sleep(1);
    }
    
}

int main()
{
    std::string name = "thread-1";
    std::thread mythread(threadRun, name ,10);
    while (true)
    {
        std::cout << "main thread..." << std::endl;
    }
    
    mythread.join();
    return 0;
}

C++内部封装的多线程的东西在编译的时候也要加线程库,否则编不过(C++11线程库的本质就是对原生线程库接口的封装)

当年说文件的时候也是这样的


http://www.kler.cn/news/311711.html

相关文章:

  • UAC2.0 麦克风——音量控制
  • etcd之etcd简介和安装(一)
  • 全面整理的Oracel 数据库性能调优方案
  • 关系运算符
  • vue选项式写法项目案例(购物车)
  • 制作网上3D展馆需要什么技术并投入多少费用?
  • JSP分页功能实现案例:从基础到应用的全面解析
  • python SQLAlchemy 数据库连接池
  • 《拿下奇怪的前端报错》序章:报错输出个数值数组Buffer(475) [Uint8Array],我来教它说人话!
  • 【Unity】检测鼠标点击位置是否有2D对象
  • Modbus_tcp
  • 数据结构-3.2.栈的顺序存储实现
  • 3.数据类型
  • 算法打卡 Day41(动态规划)-理论基础 + 斐波那契数 + 爬楼梯 + 使用最小花费爬楼梯
  • python脚本转mac app+app签名公正
  • 开源 AI 智能名片 S2B2C 商城小程序与正能量融入对社群归属感的影响
  • python 实现armstrong numbers阿姆斯壮数算法
  • 利用pandas为海量数据添加UUID并实现精准筛选
  • 开放标准如何破解企业数字化与可持续发展的困境:The Open Group引领生态系统架构创新
  • 新电脑工作流搭建记录-前端篇
  • 《ElementUI/Plus 基础知识》el-table + sortablejs 实现 row 拖动改变顺序(Vue2/3适用)
  • C++对C的扩充
  • 二百六十六、Hive——Hive的DWD层数据清洗、清洗记录、数据修复、数据补全
  • ros跨平台订阅和发布消息(ip如何设置)
  • Springboot的三层架构
  • ⭐ Unity + OpenCV 实现实时图像识别与叠加效果
  • HTML基础和常用标签
  • 【C++笔记】八、结构体 [ 3 ]
  • 如何着手创建企业数据目录?(一)数据目录的设定
  • python 实现area under curve曲线下面积算法