目录
基本概念
重新理解进程
线程真实存在吗?
问题解答
线程资源
线程控制
线程创建
如何全面看待线程函数传参
如何看到线程函数返回
线程查询
线程等待
线程终止
线程分离
基本概念
线程(thread)是指在单个进程内,多路并行执行的创建和管理单元;
重新理解进程
在没有学习线程的时候,我们学的进程=内核数据结构+程序的代码和数据;每个进程系统都会为其分配task_struct(PCB),mm_struct(虚拟地址空间),页表来描述和管理进程;
上面的整个才是进程,进程是程度系统资源分配的基本实体;
而线程其实就是进程内的执行流;和之前的进程对比,之前的进程就是内部只有一个执行流的进程!
线程真实存在吗?
在windows中存在真正的进程tcb,而在Linux中并不存在真正的进程,Linux中是用进程来模拟线程!!!
因为复原PCB,同PCB来统一表示执行流,这样的话就不需要为线程单独设计数据结构和调度算法了;像进程一样,线程在程序中有独立,并发的执行路径,每个线程都有它自己私有的栈空间,自己的程序计数器,自己的寄存器,但是他们共享全局数据区,文件描述符等;
问题解答
既然线程用PCB来模拟,那CPU会不会区分task_struct是线程还是进程呢?
不做区分,CPU统一将他们看做执行流;Linux中执行流统一被称为轻量级进程;
那么,既然有了多进程,为什么要有多线程呢?
- 进程的创建成本高,创建线程的成本低
- 线程调度成本低
- 删除线程成本低
为什么说线程调度成本低呢?
主要原因是在进程切换时,由于线程共享进程的内存空间和资源,cpu的cache中数据往往有效,因此减少了因cache不命中导致的缺页中断开销。
既然线程这么好用,为什么要有进程呢?
虽然线程有很多优势,但也不是毫无代价的.事实上,有些最可怕的bug就是由多线程引起的.设计,编写,理解以及最重要的----调试多线程程序,这些复杂度都是远远高于单个线程的进程. 最主要的原因:多个虚拟的处理器,但是只有一个虚拟化内存实例,当一个线程同步失败,就会使整个进程运行出错以及程序崩溃;
线程资源
线程共享资源:
- 进程地址空间
- 文件描述符表
- 信号处理方式
- 当前工作目录
- 用户ID和组ID
线程私有资源:
- 线程ID
- 私有栈空间(重要)
- 一组寄存器(重要)
- 程序计数器
- errno变量
- 调度优先级
- 信号屏蔽字
线程控制
线程创建
pthread_create:创建线程
#include <iostream>
#include <unistd.h>
#include<pthread.h>
using namespace std;
void *pthreadRun(void *args)
{
while(true)
{
cout<<"I am "<<(const char *)args<<endl;
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,pthreadRun,(void *)"thread-1");
while(true)
{
cout<<"I am main thread"<<endl;
sleep(1);
}
return 0;
}
运行结果:
如何全面看待线程函数传参
给线程传参可以是任意类型,也可以是类对象类型;
void* 指针可以指向任意类型的数据;
具体的void*内容可以看下面的文章:
c语言---指针(1)-CSDN博客
(1)
运行结果:
(2)不推荐
运行结果:
既然不推荐的话,怎么优化呢?
ThreadData *td =new ThreadData();
td->name="thread";
td->num="1";
如何看到线程函数返回
- 只考虑正确的返回,不考虑异常,因为异常了,整个进程就崩溃了,包括主线程
- 可以传任意参数,也可以是任意类对象
运行结果:
线程查询
怎么查询线程有没有创建成功呢?
ps -aL:查询线程
ps ajx:查询进程
我们会发现同一个进程下创建的多线程的PID是一样的,但是每个线程的LWP是不一样的;其实OS调度时看到是LWP并不是PID,我们在没有学习线程的时候说的是PID,和现在的说法不是冲突了吗?其实并不是我们之前创建的不管是多进程还是单进程,在进程中都是只有一个执行流的,那是PID和LWP是相同的,所以我们之前所说的PID,其实看到还是LWP只不过LWP和PID一样;学完线程后,我们以后就要说LWP了;
怎么查询线程的LWP呢?
void *pthreadRun(void *args)
{
cout<<"tid :"<<pthread_self()<<endl;
while(true)
{
cout<<"I am "<<(const char *)args<<endl;
sleep(1);
}
return nullptr;
}
运行结果:
线程等待
我们期望主线程先退出还是新线程最后退出? ----> 当然是主线程了,那如何保证main thread最后退出呢?
join来保证,这时就要用到pthread_join来等待新线程;
int main()
{
pthread_t tid;
int n=pthread_create(&tid,nullptr,pthreadRun,(void *)"thread-1");
if(n!=0)
{
cout<<strerror(errno)<<endl;
return 1;
}
//期望谁最后退出?主线程,还是新线程 -->main thread 如何保证?
n = pthread_join(tid,nullptr);
if(n==0)
{
cout<<"main thread wait sucess"<<endl;
}
return 0;
}
运行结果:
线程终止
在进程的学习时,我们学过进程的终止有:
- return
- exit
- 信号
那线程的终止有哪些呢?
1、自然退出:线程函数return
2、pthread_exit() 我们不能用exit()来进行线程的退出,如果一个线程退出时,用了exit,那么整个进程就会退出,因为exit是用来终止进程的;
3、pthread_cancel :取消一个线程
线程分离
既然进程有SIGCHLD信号可以让进程不等待,那线程有没有类似的可以让线程也不等待?
当然有了,就是pthread_detach;
- 一个线程被创建默认是joinable的,必须被join
- 如果一个线程被分离,线程的工作状态处于分离状态,那么就不需要/不必要被join的,但是依旧属于进程内部,只是不需要等待了而已;分离不等于分家;
以上就是线程的基本概念,线程控制的全部内容!!!