Linux多线程(六)之线程控制4【线程ID及进程地址空间布局】

文章目录

线程ID及进程地址空间布局

  1. pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。

    该线程ID和前面说的线程ID不是一回事。

  2. 前面讲的线程ID属于进程调度的范畴。

​ 因为线程是轻量级进程,是操作系统调度器的最小单位,

​ 所以需要一个数值来唯一表示该线程。

​ 用户级线程+内核轻量级进程=Linux线程

​ 线程:用户级线程、内核级线程

Linux线程就是用户级线程

​ 用户级执行流:内核lwp = 1:1

  1. pthread_ create函数第一个参数指向一个虚拟内存单元,

​ 该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。

​ 线程库的后续操作,就是根据该线程ID来操作线程的。

  1. 线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID:

    pthread_t pthread_self(void);

c++ 复制代码
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <thread>
#include <string>
#include <cstdlib>

using namespace std;

string tohex(pthread_t tid)
{
    char hex[64];
    snprintf(hex, sizeof(hex), "%p", tid);
    return hex;
}

void *threadroutine(void *args)
{
    while (1)
    {
        cout << "thread id: " << tohex(pthread_self()) << endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadroutine, (void *)"thread 1");
    cout << "main thread create thread done,new thread id: " << tohex(tid) << endl;
    pthread_join(tid, nullptr);
    return 0;
}

pthread_t 到底是什么类型呢?取决于实现。

对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,

本质就是一个进程地址空间上的一个地址。

一个执行流本质就是一条调用链。

调用函数就是在栈帧中为该函数形成栈帧结构。

为了完成临时变量空间在栈帧中的开辟和释放。

验证创建多线程:

makefile

shell 复制代码
mythread:mythread.cc
	g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -rf mythread

mythread.cc

c++ 复制代码
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <cstring>
#include <cstdlib>
#include <vector>

using namespace std;

#define NUM 5

struct threadData
{
    string tname;
};

string toHex(pthread_t tid)
{
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "0x%x", tid);
    return buffer;
}

void InitThreadData(threadData *td, int i)
{
    td->tname = "thread-" + to_string(i);
}

void *threadRoutine(void *args)
{
    threadData *td = static_cast<threadData *>(args);
    int i = 0;
    while (i < 5)
    {
        cout << "pid: " << getpid() << " ,tid: " << toHex(pthread_self()) << " ,name: " << td->tname << endl;
        sleep(1);
        i++;
    }
    delete td;
    return nullptr;
}

int main()
{
    vector<pthread_t> tids;
    for (int i = 0; i < NUM; i++)
    {
        pthread_t tid;
        threadData *td = new threadData;
        // 使用堆空间 而且每次都是新的堆空间 堆空间是共享的 访问的是堆空间的不同位置
        // 我们让一个线程访问对应的一个堆空间
        InitThreadData(td, i);
        pthread_create(&tid, nullptr, threadRoutine, td);
        tids.push_back(tid);
        sleep(1);
    }

    for (int i = 0; i < NUM; i++)
    {
        pthread_join(tids[i], nullptr);
    }

    return 0;
}

验证每个线程都有独立的栈结构:

c++ 复制代码
void *threadRoutine(void *args)
{
    int test_i=0;
    threadData *td = static_cast<threadData *>(args);
    int i = 0;
    while (i < 5)
    {
        cout << "pid: " << getpid() << " ,tid: " << toHex(pthread_self()) 
        << " ,name: " << td->tname <<" ,test_i: "<<test_i<<" ,&test_i: "
        <<toHex((pthread_t)&test_i)<< endl;
        sleep(1);
        i++,test_i++;
    }
    delete td;
    return nullptr;
}

验证每个线程的栈数据是可以被访问的(但是不建议):

c++ 复制代码
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <cstring>
#include <cstdlib>
#include <vector>

using namespace std;

#define NUM 5

int*p;

struct threadData
{
    string tname;
};

string toHex(pthread_t tid)
{
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "0x%x", tid);
    return buffer;
}

void InitThreadData(threadData *td, int i)
{
    td->tname = "thread-" + to_string(i);
}

void *threadRoutine(void *args)
{
    int test_i=0;
    threadData *td = static_cast<threadData *>(args);
    int i = 0;
    if(td->tname=="thread-0")
    {
        p=&test_i;
    }
    while (i < 5)
    {
        cout << "pid: " << getpid() << " ,tid: " << toHex(pthread_self()) 
        << " ,name: " << td->tname <<" ,test_i: "<<test_i<<" ,&test_i: "<<&test_i<< endl;
        sleep(1);
        i++,test_i++;
    }
    delete td;
    return nullptr;
}

int main()
{
    vector<pthread_t> tids;
    for (int i = 0; i < NUM; i++)
    {
        pthread_t tid;
        threadData *td = new threadData;
        // 使用堆空间 而且每次都是新的堆空间 堆空间是共享的 访问的是堆空间的不同位置
        // 我们让一个线程访问对应的一个堆空间
        InitThreadData(td, i);
        pthread_create(&tid, nullptr, threadRoutine, td);
        tids.push_back(tid);
        // sleep(1);
    }

    sleep(1);//确保赋值成功
    cout<<"main thread get a thread local value,val: "<<*p<<" ,&val: "<<p<<endl;


    for (int i = 0; i < NUM; i++)
    {
        pthread_join(tids[i], nullptr);
    }

    return 0;
}

线程与线程之间几乎没有秘密

线程栈上的数据,也是可以被其他线程看到并访问的。

线程局部存储
c++ 复制代码
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <cstring>
#include <cstdlib>
#include <vector>

using namespace std;

#define NUM 5

int g_val=0;

struct threadData
{
    string tname;
};

string toHex(pthread_t tid)
{
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "0x%x", tid);
    return buffer;
}

void InitThreadData(threadData *td, int i)
{
    td->tname = "thread-" + to_string(i);
}

void *threadRoutine(void *args)
{
    threadData *td = static_cast<threadData *>(args);
    int i = 0;
    while (i < 5)
    {
        cout << "pid: " << getpid() << " ,tid: " << toHex(pthread_self()) 
        << " ,name: " << td->tname <<" ,g_val: "<<g_val<<" ,&g_val: "<<&g_val<< endl;
        sleep(1);
        i++,g_val++;
    }
    delete td;
    return nullptr;
}

int main()
{
    vector<pthread_t> tids;
    for (int i = 0; i < NUM; i++)
    {
        pthread_t tid;
        threadData *td = new threadData;
        InitThreadData(td, i);
        pthread_create(&tid, nullptr, threadRoutine, td);
        tids.push_back(tid);
        // sleep(1);
	}
    for (int i = 0; i < NUM; i++)
    {
        pthread_join(tids[i], nullptr);
    }

    return 0;
}

全局变量是被所有的线程同时看到并访问的。

如果线程想要一个私有的全局变量呢??

复制代码
__thread+变量
__thread int g_val=0;

编译器提供的编译选项

复制代码
__thread threadData td;

__thread只能用来定义内置类型,不能用来定义自定义类型

作用:可以存储该线程的某些系统调用数据(就不需要频繁调用的系统调用了)。

c++ 复制代码
__thread unsigned int number=0;
__thread int pid=0;

struct threadData
{
    string tname;
};

void *threadRoutine(void *args)
{
    pid=getpid();
    number=pthread_self();
    threadData *td = static_cast<threadData *>(args);
    int i = 0;
    while (i < 5)
    {
        cout<<"pid: "<<pid<<" ,tid: "<<number<<endl;
        sleep(1);
        i++,g_val++;
    }
    delete td;
    return nullptr;
}

在调用链上,可以直接获取该线程的pid、tid等。(线程级别的全局变量而且互不干扰)

相关推荐
碎梦归途24 分钟前
Linux_T(Sticky Bit)粘滞位详解
linux·运维·服务器
HHBon28 分钟前
判断用户输入昵称是否存在(Python)
linux·开发语言·python
Paper_Love31 分钟前
Linux-pcie ranges介绍
linux
DjangoJason1 小时前
计算机网络 : 应用层自定义协议与序列化
linux·服务器·计算机网络
敢敢变成了憨憨2 小时前
java操作服务器文件(把解析过的文件迁移到历史文件夹地下)
java·服务器·python
苇柠2 小时前
Java补充(Java8新特性)(和IO都很重要)
java·开发语言·windows
鑫鑫向栄2 小时前
[蓝桥杯]剪格子
数据结构·c++·算法·职场和发展·蓝桥杯
白总Server2 小时前
C++语法架构解说
java·网络·c++·网络协议·架构·golang·scala
安科瑞刘鸿鹏2 小时前
破解高原运维难题:分布式光伏智能监控系统的应用研究
运维·物联网·安全
羊儿~2 小时前
P12592题解
数据结构·c++·算法