一、理解线程id
首先我们要知道给用户提供的线程id不是内核里面LWP(轻量级进程id),而是pthread库自己维护的一个唯一值。
我们理解为什么线程id不是内核里面LWP,因为用户没有权限使用内核里面的字段,那是专门给OS管理线程用的。
但是为什么要pthread库来帮我们用户维护用户级线程id呢?
因为Linux内核根本就不存在线程的概念,所谓用户口中的线程在Linux眼里就是轻量级进程,之后理解不在赘述具体参考文章http://t.csdnimg.cn/jTGGP中pthread库介绍。
内核级理解:
所以我们清楚的看到返回给用户的线程id就是一个线程控制块在虚拟地址空间的起始位置。
类比FILE对象,其实就在c标准库中,返回的是FILE*就是地址。
所以 pthread_join() 函数就是通过tid找到存在虚拟地址空间中的线程结构体对象,把里面的退出信息拷贝出来返回。
操作系统内示意图:
二、简单封装原生线程库
thread.hpp
#pragma once
#include<pthread.h>
#include<iostream>
#include<string>
using namespace std;
namespace Thread
{
//线程执行方法
typedef void(*func_t)(const string& name);
class Thread
{
public:
//线程执行方法
void Excute()
{
cout << _name << " is running" << endl;
_isrunning = true;
_func(_name);
_isrunning = false;
}
public:
Thread(const string& name, func_t func)
:_name(name)
,_func(func)
{
cout << "create " << name << " done" << endl;
}
//线程执行回调方法
//设成静态方法,函数就不会自带this指针,类型就匹配上了
//但是此时就无法调类的回调函数,在create函数中传参即可
static void* ThreadRoutine(void* args)
{
//获得当前线程对象
Thread* self = static_cast<Thread*>(args);
//调用线程执行方法
self->Excute();
return nullptr;
}
bool Start()
{
int n = ::pthread_create(&_tid, nullptr, ThreadRoutine, this);
if (n != 0)
return false;
return true;
}
string Status()
{
if(_isrunning)
return "running";
else
return "sleep";
}
string GetName()
{
return _name;
}
void Stop()
{
if(_isrunning = true)
{
::pthread_cancel(_tid);
_isrunning = false;
cout << _name << " stop" << endl;
}
}
void Join()
{
if(!_isrunning)
{
::pthread_join(_tid, nullptr);
cout << _name << " join done" << endl;
}
}
~Thread()
{
}
private:
string _name;
pthread_t _tid;
bool _isrunning = false;
func_t _func; //线程执行的回调函数
};
}
main.cc
#include<iostream>
#include"thread.hpp"
#include<unistd.h>
#include<vector>
using namespace Thread;
using namespace std;
void Print(const string& name)
{
int cnt = 0;
while(1)
{
cout << name << " is running, cnt: " << cnt++ << endl;
sleep(1);
}
}
const int num = 10;
int main()
{
//创建线程
vector<Thread::Thread> threads;
for(int i = 0; i < num; i++)
{
string name = "thread-" + to_string(i + 1);
threads.emplace_back(name, Print);
sleep(1);
}
//统一启动
for(auto& thread : threads)
{
thread.Start();
}
sleep(10);
//统一结束
for(auto& thread : threads)
{
thread.Stop();
}
//统一回收
for(auto& thread : threads)
{
thread.Join();
}
// Thread::Thread t("thread-1", Print);
// t.Start();
// cout << t.GetName() << " status: " << t.Status() << endl;
// sleep(10);
// cout << t.GetName() << " status: " << t.Status() << endl;
// t.Stop();
// sleep(1);
// cout << t.GetName() << " status: " << t.Status() << endl;
// t.Join();
// cout << "join done" << endl;
return 0;
}
三、线程的局部存储
对于一个全局变量,一个进程中任意线程都能进行修改,另外的线程也可以看到变化,因为线程共享进程的大部分数据。
在LInux中我们用 __thread修饰全局变量(只能修饰内置类型)就能让全局变量在所有线程各有一份,地址也不同,这就能实现每一个线程都有属于自己的变量,这就是线程的局部存储。