Linux线程控制

目录

一、线程创建

1.1 pthread_create

1.2 线程传入启动函数参数方式

二、线程退出(pthread_exit函数 pthread_cancel函数)

三、线程等待

3.1 为什么要线程等待?

3.2 pthread_join函数

 四、线程分离

4.1 pthread_detach() 和 pthread_self()

五、pthread库维护线程的基本结构


一、线程创建

1.1 pthread_create

thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码 

1.2 线程传入启动函数参数方式

关于第四个参数,我们要注意其是void*类型指针,任意指针都可以传给线程!现在我们模拟一种错误的传参方式,传栈区资源,基于线程运行顺序产生的传参问题!

#define NUM 5
void* pthread_route(void* args)
{
    const char* name = (const char*)args;
    cout << "new thread create success, its name: "<<name<<endl;
}

int main()
{
    pthread_t pid;
    for(int i=0;i<NUM;i++)
    {
        char buffer[64];
        snprintf(buffer,sizeof(buffer),"thread %d",i+1);
        pthread_create(&pid,nullptr,pthread_route,buffer);
    }
    sleep(1);
    return 0;
}

运行结果: 

结果显示,我们的线程名字都是一样的!

首先我们可以知道这样的结果说明我们传过去的buffer地址是同一个地址! 有人可能疑惑不是每次循环后buffer的地址应该每次不一样吗? 这里结合函数栈帧知识,发现并没有函数调用,所以每次都还是在main函数栈帧内,所以每次的地址其实循环进来依旧是原来的地址!

有人又会提出,不对啊,按道理应该数字是根据i+1的来啊,按照我们route函数思路不应该全是5啊!

这就说明事实不是按照我们的所想,main函数创建线程后并不是立马执行线程对应的函数,当main函数创建完所有线程后,才执行线程的代码!而此时配合前面buffer首地址没变,而里面的数据再不断变换导致!

所以我们创建线程如果想看到线程私有的东西,不能传入共享的数据!我们不能传入栈内资源(这里没有多想其他场景),所以我们应该利用堆区资源!


正确的传参方式:数据放入堆区,传堆区指针!

#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
#define NUM 3
using namespace std;

//每个线程独立的资源 线程ID + 线程buffer
class Thread_data
{
public:
    pthread_t _pid;
    char namebuffer[64];
};

void *thread_route(void *args)
{
    Thread_data *p = static_cast<Thread_data *>(args);
    int cnt = 3;
    while (cnt--)
    {
        cout << "new thread create success,its name: " << p->namebuffer << " &cnt: " << &cnt <<" cnt= "<<cnt<<endl;
    }
    delete p;
    return nullptr;
}

int main()
{
    for (int i = 0; i < NUM; i++)
    {
        Thread_data *pt = new Thread_data();
        snprintf(pt->namebuffer, sizeof(pt->namebuffer), "thread %d", i + 1);
        pthread_create(&pt->_pid, nullptr, thread_route, pt);
    }
    sleep(1);
    return 0;
}

运行结果: 


二、线程退出(pthread_exit函数 pthread_cancel函数)

void pthread_exit(void *value_ptr);
参数
value_ptr:value_ptr不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

pthread_cancel  功能:取消一个执行中的线程
int pthread_cancel(pthread_t thread);
参数
thread:线程ID
返回值:成功返回0;失败返回错误码


三、线程等待

3.1 为什么要线程等待?

为什么要有线程等待?回想以前的进程等待,父进程回收子进程,获取子进程退出信息!

对于已经退出的线程,其空间没有被释放,仍在进程的地址空间内!所以创造新的线程不会复用刚才退出线程的地址空间,这就造成了资源的浪费!

3.2 pthread_join函数

原型:
int pthread_join(pthread_t thread, void **value_ptr);
参数:
thread:线程ID
value_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码

线程的调用逻辑函数返回值是void*,该函数会在线程结束前将线程结束状态码保存进pthread库中,打个比方它的数字状态码是10,然后通过强转(void*)10,将4字节整形放入8字节的指针中(Linux下指针默认是64位下!)。外部获取函数内部结果,我们需要传地址,所以要传void**!

需要知道的是,我们返回的值一定是右值!也就输说常变量、堆空间资源可以返回!栈上数据不能返回因为函数栈帧结束后栈空间数据会被销毁!

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

using namespace std;
#define NUM 5
class pthread_data
{
public:
    pthread_t tid;
    char buffer[128];
};
class Thread_ret
{
public:
    int exit_code;
    int exit_result;
};

void* pthread_route(void* args)
{
    Thread_ret* ret = new Thread_ret();
    int cnt = 5;
    while(cnt)
    {
        cout <<"cnt: " << cnt-- <<" &cnt:" <<&cnt<<endl;//BUG?
        sleep(1);
    }
    ret->exit_code = 106;
    ret->exit_result = 0;
    return (void*)ret;
}
int main()
{
    vector<pthread_data*> vp;

    for(int i=0;i<NUM;i++)
    {
        pthread_data* pd = new pthread_data();
        pthread_create(&pd->tid,nullptr,pthread_route,pd);
        snprintf(pd->buffer,sizeof(pd->buffer),"thread :%d pid:0x%x ",i+1,pd->tid);
        vp.push_back(pd);
    }

    for(auto& e : vp)
    {
        cout << "creat thread success: " << e->buffer<<endl;
    }

    for(auto&e :vp)
    {
        Thread_ret* ret;
        pthread_join(e->tid,(void**)(&ret));
        cout <<"join :" <<e->buffer<<" success " <<"exit code:" <<ret->exit_code <<" exit result: " <<ret->exit_result<<endl;
        delete e;
    }

    cout <<"main quit!"<<endl;
    return 0;
}


 四、线程分离

1.默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
2.如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。

4.1 pthread_detach() 和 pthread_self()

分离线程,可以是指定的其他线程,也可以是自己!

int pthread_detach(pthread_t thread);

pthread_self()获取线程自己的ID!


五、pthread库维护线程的基本结构

在前面我们了解到线程有属于自己的ID、私有栈等等结构,这些线程肯定需要被组织!如何组织呢?在Linux下线程库内用结构体(TCP)对每个线程进行组织!库其实本质是磁盘上的文件,在链接的时候将内容加载到内存的共享区内,也就是说我们的线程的属性存储在共享区内!线程的私有栈区实在共享区内!

 线程的局部存储:可以使一个全局变量让所有线程私有栈内创建一份!

在全局变量前面加上__thread 即可 !


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值