文章目录
- 线程ID及进程地址空间布局
- 线程局部存储
线程ID及进程地址空间布局
-
pthread_ create
函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。
-
前面讲的线程ID属于进程调度的范畴。
因为线程是轻量级进程,是操作系统调度器的最小单位,
所以需要一个数值来唯一表示该线程。
用户级线程+内核轻量级进程=Linux
线程
线程:用户级线程、内核级线程
Linux
线程就是用户级线程
用户级执行流:内核lwp
= 1:1
pthread_ create
函数第一个参数指向一个虚拟内存单元,
该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。
线程库的后续操作,就是根据该线程ID来操作线程的。
- 线程库NPTL提供了
pthread_ self
函数,可以获得线程自身的ID:
pthread_t pthread_self(void);
#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
mythread:mythread.cc
g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -rf mythread
mythread.cc
#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;
}
验证每个线程都有独立的栈结构:
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;
}
验证每个线程的栈数据是可以被访问的(但是不建议):
#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;
}
线程与线程之间几乎没有秘密
线程栈上的数据,也是可以被其他线程看到并访问的。
线程局部存储
#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
只能用来定义内置类型,不能用来定义自定义类型
作用:可以存储该线程的某些系统调用数据(就不需要频繁调用的系统调用了)。
__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
等。(线程级别的全局变量而且互不干扰)