线程(一) linux
目录
线程概念
资源划分
共享资源与私有资源
私有
共享
优缺点
优点
缺点
linux下的线程
LWP(轻量级进程 Light Weight Progress)
线程控制
线程创建
线程等待
线程退出
获取线程ID
线程分离
简单封装
小知识
线程概念
进程:承担分配系统资源的实体
线程:操作系统调度的基本单位,是进程内部的执行流
不同的线程会被分配去执行不同的函数,作为一个进程内部的多个执行流
资源划分
函数可以看做虚拟地址的集合,不同的线程执行不同的函数相当于拥有不同的虚拟地址资源
共享资源与私有资源
私有
1.线程ID
2.线程的上下文数据
3.栈
4.errno
5.信号屏蔽字
6.调度优先级
共享
1.因为线程在同一地址空间中,所以全局资源(全局函数,全局变量)都是共享的,这里会出现可重入函数的问题
2.文件描述符表
3.信号处理方式
4.当前工作目录
5.用户id和组id
优缺点
优点
1.创建线程比创建进程代价小
2.与进程切换相比,线程切换工作少
因为:
(1).线程切换时,无需切换CR3(CR3中存进程的页目录表基地址)
(2).TLB(缓存) 缓存的数据在线程切换时无需变化,进程切换时要更新
(3).线程切换时cache缓存不用更新,进程切换要更新
3.占用资源比进程少
4.能充分利用多处理器的可并行数量
5.在等待IO操作结束的时候,程序可以执行其他任务
6.对于计算密集型应用,可以将计算分解到多个线程中来在多处理器上并行
7.IO密集型应用,线程可以同时等待不同的IO操作,将IO操作重叠来提高性能
缺点
1.太多线程导致性能损失
2.健壮性降低,一个线程出问题,整个进程出问题
3.缺乏访问控制
进程是访问控制的基本单位,一个线程调用某些OS函数会对整个进程造成影响
linux下的线程
1.linux中,内核中,线程的实现是用进程模拟的,复用了进程代码和结构
2.线程在进程的虚拟地址空间中运行,不同的线程访问虚拟地址空间中的一部分资源,通过不同线程执行不同的入口函数实现
LWP(轻量级进程 Light Weight Progress)
linux通过轻量级进程来模拟线程,就是在内核级,linux使用轻量级进程作为线程,只提供轻量级进程的系统调用
而为了适应环境(大多数人都使用线程,而不熟悉轻量级进程),通过外部库封装了一个原生线程库pthread
线程控制
注意:1.使用线程控制函数要引入<pthread.h>头文件
2.链接时要链接pthread库,加' -lpthread'选项
线程创建
pthread_create
thread:输出型参数,线程id 一个无符号长整数(unsigned long int) ,实际上是pthread线程库维护的线程结构体对象的起始地址
attr:线程属性,一般传nullptr即可
start routine:该线程执行的函数
arg:传递给该线程的参数,也就是给函数的参数
返回值 :成功返回0,失败返回错误码
线程中可以创建线程,这个新线程也是由进程创建的
ps -aL 查看进程
线程等待
pthread_join
thread: 线程id,就是pthread库中表明线程控制块的起始地址
retval :输出参数,获取新线程退出信息,不关心传nullptr即可
返回值 :成功返回0,失败返回错误码
join会阻塞等待线程执行完毕,主线程最后退出,自动解决子线程僵尸问题
线程退出
四种方式
1.exit 直接终止进程
2.线程函数return
3.pthread_exit 将使用此函数的线程退出
retval :输出参数,获取新线程退出信息,不关心传nullptr即可
4.pthread_cancel 会将指定线程退出
thread: 线程id,就是pthread库中表明线程控制块的起始地址
获取线程ID
pthread_self
线程分离
分离线程:如果不关心线程的返回值,那么使用这个告诉系统,线程退出时,自动释放资源,无需join
默认情况下,新创建的线程是joinable的,线程退出后,需要进行pthread_jioin操作,否则无法释放资源,导致资源泄露
1.如果让子线程自己去分离,因为主线程和多个子线程执行先后不确定,所以要让想办法子线程先执行
2.如果让主线程去分离,在适当位置分离即可
简单封装
#pragma once
#include <iostream>
#include <thread>
#include <functional>
#include <string>
#include <unistd.h>
#include <sys/syscall.h>
#define get_lwp_id() syscall(SYS_gettid)
using func_t = std::function<void()>;
const std::string threadname = "name";
class Thread
{
bool _running;
pthread_t _tid;
pid_t _lwpid;
std::string _name;
func_t _func;
public:
Thread(func_t func,const std::string &name = threadname)
:_name(name)
,_func(func)
,_running(false)
{}
~Thread(){}
static void *start_routine(void *args)
{
Thread *self = static_cast<Thread*>(args);
self->_running = true;
self->_lwpid = get_lwp_id();
self->_func();
pthread_exit((void*)0);
}
void start()
{
int tmp = pthread_create(&_tid,nullptr,start_routine,this);
if(tmp==0) {std::cout<<"pthread_start success"<<std::endl;}
}
void Join()
{
if(!_running) return;
int tmp = pthread_join(_tid,nullptr);
if(tmp==0) {std::cout<<"pthread_join success"<<std::endl;}
}
};
使用举例
#include "thread.hpp"
#include <vector>
#include <iostream>
void test()
{
for(int i=0;i<5;i++)
{
std::cout<<"runing......"<<std::endl;
sleep(0.5);
}
}
int main()
{
std::vector<Thread> threads;
for(int i=0;i<5;i++)
{
std::string name = "thread-";
name += std::to_string(i+1);
Thread t(test,name);
threads.push_back(t);
}
for(int i=0;i<5;i++)
{
threads[i].start();
}
for(int i=0;i<5;i++)
{
threads[i].Join();
}
return 0;
}
小知识
1.windows中的线程专门定义了线程结构体及其调度算法
2.多个子线程执行先后顺序不定
3.linux线程库叫做用户级线程,因为使用的是用户级别的pthread库
4.主线程使用进程的栈(主线程的栈),子线程使用库内维护的开的新栈,子线程的栈是固定大小的,不会向下增长
5.线程管理不是进程为单位的(每个进程管着自己的线程),是全部系统中的lwp都由pthread库来管理
6.__thread 变量 编译器提供的编译修饰符(内置选项)
将变量声明为子线程自己的变量, 只支持内置类型