线程控制及线程底层原理

thread id 本质是一个地址。

以十六机制打印id。

 线程终止的两种方法。

1.直接return;

2.pthread_exit();

注意exit()是用来终止进程的,不能用于线程。

那怎么获取线程的返回值呢?

首先,和进程一样,线程退出也需要等待,不然也会导致僵尸问题。

int pthread_join(pthread_t thread, void **retval);
args:
    pthread_t thread: 被连接线程的线程号
    void **retval : 指向一个指向被连接线程的返回码的指针的指针
return:
    线程连接的状态,0是成功,非0是失败 

#include<iostream>
#include<string>
#include<functional>
#include<vector>
#include<pthread.h>
#include<unistd.h>
#include<time.h>
using namespace std;
using func_t=function<void()>;
const int threadnum=5;
class ThreadData
{
    public:
    ThreadData(const string&name,const uint64_t&ctime,func_t f)
    :threadname(name)
    ,createtime(ctime)
    ,func(f)
    {}
    public:
    string threadname;
    uint64_t createtime;
    func_t func;
};
string to_Hex(pthread_t tid)
{
    char id[64];
    snprintf(id,sizeof id,"0x%x",tid);
    return id;
}
void *ThreadRoutine(void*args)
{
    int a=2;
    ThreadData*td=static_cast<ThreadData*>(args);
    while(a--)
    {
        cout<<"I am new Thread  "<<td->threadname<<' '<<td->createtime<<' '<<to_Hex(pthread_self())<<' '<<endl;
        td->func();
        // if(td->threadname=="thread-4")
        // {
        //     a/=0;
        // }
        sleep(1);
    }
    pthread_exit((void*)"thread 1");

}
void print()
{
    cout<<"我是线程执行的大任务的一部分"<<endl;
}

int main()
{
  
    pthread_t tid;
    char threadname[64];
    snprintf(threadname,sizeof threadname,"%s-%d","thread",1);
    ThreadData*td=new ThreadData(threadname,uint64_t(time(nullptr)),print);
    pthread_create(&tid,nullptr,ThreadRoutine,td);
    cout<<to_Hex(tid)<<endl;
    void *ret=nullptr;
    int n=pthread_join(tid,&ret);
    cout<<"main thread done"<<"   n:   "<<n<<' '<<(const char*)ret<<endl;
    return 0;

}

我们要要得到为void*的返回值,那么就需要传入void**,所以是&ret。 

线程的等待不需要像进程一样获取异常,因为一出问题就挂。

#include<iostream>
#include<string>
#include<functional>
#include<vector>
#include<pthread.h>
#include<unistd.h>
#include<time.h>
using namespace std;
using func_t=function<void()>;
const int threadnum=5;
class ThreadData
{
    public:
    ThreadData(const string&name,const uint64_t&ctime,func_t f)
    :threadname(name)
    ,createtime(ctime)
    ,func(f)
    {}
    public:
    string threadname;
    uint64_t createtime;
    func_t func;
};
class ThreadReturn
{
    public:
    ThreadReturn(pthread_t id,int code,string info)
    :_id(id)
    ,_code(code)
    ,_info(info)
    {}
    public:
    pthread_t _id;
    int _code;
    string _info;
};
string to_Hex(pthread_t tid)
{
    char id[64];
    snprintf(id,sizeof id,"0x%x",tid);
    return id;
}
void *ThreadRoutine(void*args)
{
    int a=2;
    ThreadData*td=static_cast<ThreadData*>(args);
    while(a--)
    {
        cout<<"I am new Thread  "<<td->threadname<<' '<<td->createtime<<' '<<to_Hex(pthread_self())<<' '<<endl;
        td->func();
        // if(td->threadname=="thread-4")
        // {
        //     a/=0;
        // }
        sleep(1);
    }
    ThreadReturn* ret=new ThreadReturn(pthread_self(),10,"quit normal");
    //pthread_exit((void*)ret);
    return ret;

}
void print()
{
    cout<<"我是线程执行的大任务的一部分"<<endl;
}


int main()
{
  
    pthread_t tid;
    char threadname[64];
    snprintf(threadname,sizeof threadname,"%s-%d","thread",1);
    ThreadData*td=new ThreadData(threadname,uint64_t(time(nullptr)),print);
    pthread_create(&tid,nullptr,ThreadRoutine,td);
    cout<<to_Hex(tid)<<endl;
    void *ret=nullptr;
    int n=pthread_join(tid,&ret);
    ThreadReturn*r=static_cast<ThreadReturn*>(ret);
    //cout<<"main thread done"<<"   n:   "<<n<<' '<<(ThreadReturn*)ret<<endl;
    cout<<r->_id<<' '<<r->_code<<' '<<r->_info<<endl;
    return 0;

}

线程的返回值,可以是结构化的数据。

线程的等待默认是joinable的,如果阻塞等待失败可能会有僵尸问题。

我们可以把它设置成分离状态。

实际上,像是QQ这样的软件,死循环运行才是常态。

为了让线程分离,我们可以调用pthread_detach函数。

  • 函数描述:实现线程分离
  • 函数原型:int pthread_detach(pthread_t thread);
  • 函数返回值:成功:0;失败:错误号

也可用pthread_cancel(pthread_t tid)直接取消进程。 

pthread原生线程库中会有一个存储tcb(线程控制模块)的数组,用户和系统之间,线程与lwp一一对应。

tcb组成:

1、threadID:线程的唯一标识。
2、status:线程的运行状态
3、register:线程关于CPU中寄存器的情况
4、PC程序计数器:线程执行的下一条指令的地址
5、优先级:线程在操作系统调度的时候的优先级
6、线程的专属存储区:线程单独的存储区域
7、用户栈:线程执行的用户方法栈,用来保存线程当前执行的用户方法的信息
8、内核栈:线程执行的内核方法栈,用来保存线程当前执行的内核方法信息

线程的上下文是由操作系统以轻量级进程的方式保存在pcb之中的。

栈的寄存器只有一套,怎么兼顾多个线程呢?

创建轻量级进程用到的函数,是pthread_create()底部封装的函数。

第一个参数是回调方法, 第二个参数是用户传入的栈,允许用户指定栈。

每个新线程的栈,在库中维护。

C语言缓冲区,每一个C语言文件对象中会存在一个缓冲区,所以我们说缓冲区是C语言维护的,

说白了,就是在C库中维护的。

所以,一个库可以对一块内存空间进行维护。

同样的,pthread库也是库,在库中实现pthread_create的时候,内部包的就是clone函数。

在调它之前,先malloc申请空间,然后把空间传给stack,充当新线程的栈。

库一般被加载到堆栈之间的共享区。

pthread一旦被加载到内存中,要经过页表映射,最终映射到当前进程的共享区。

那么线程的栈在哪里呢?

pthread内部会帮我们new出来一段空间,把堆空间的地址写到pthread库里面,在库里用指针维护,线程退出时把空间释放掉就行。

默认地址空间中的栈由主线程使用,这样,可以保证每一个新线程都可以保存要执行的方法,要传递的参数以及动态运行时的临时变量。  

和可执行程序一样,pthread库会通过页表从内存映射到地址空间堆栈之间的共享区。

创建线程的时候,在正文代码区,直接跳转到共享区的库里,执行创建线程的函数,然后返回。

而线程库是共享的,所以,内部要管理整个系统,多个用户启动的所有的线程。

如图,共享区里有动态库和有线程结构体组成的数组。

调用线程时,将结构体中的数据传入clone函数。 

也可调用pthread_join从中获取返回值。

tid代表线程属性集合在库中的地址。

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈大大陈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值