目录
[1 线程理解](#1 线程理解)
[1.1 什么是进程](#1.1 什么是进程)
[1.2 什么是线程](#1.2 什么是线程)
[1.3 附言](#1.3 附言)
[2 线程的优点](#2 线程的优点)
[3 线程的缺点](#3 线程的缺点)
[4 线程异常](#4 线程异常)
[5 线程用途](#5 线程用途)
[6 进程与线程](#6 进程与线程)
引入
(1)创建一个线程
#include <iostream>
#include <unistd.h>
#include <pthread.h>
// 线程执行入口
void* thread_func(void* args)
{
std::string str = (char*)args;
// 第二个循环
while(true)
{
printf("Second thread:%s pid:%d num:%d\n", str.c_str(), getpid(), num);
sleep(1);
}
}
int main()
{
pthread_t tid;
// 创建线程
pthread_create(&tid, nullptr, thread_func, (void*)"pthread-1");
// 主线程
// 第一个循环
while(true)
{
printf("Main thread pid:%d num:%d\n", getpid(), num);
sleep(1);
}
return 0;
}
(2)查看轻量级进程
ps -aL
lz@VM-8-15-ubuntu:~$ ps -aL | head -1 && ps -aL | grep thread
PID LWP TTY TIME CMD
1550414 1550414 pts/0 00:00:00 thread
1550414 1550415 pts/0 00:00:00 thread
- PID:进程的PID
- LWP:轻量级进程的PID
1 线程理解
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程内部的一个执行流,在进程的虚拟地址空间中运行
- 每个函数在被编译后,都会有属于自己的一批虚拟地址空间
- 每个线程都可以访问虚拟地址空间中的资源,因此可以让不同的线程执行不同的入口函数
Linux系统内核中,线程的实现是用进程模拟的,复用了进程的代码和结构

1.1 什么是进程
进程是承担分配系统资源的基本实体,包括所有的PCB、虚拟地址空间、页表等一个大的集合
在进程内部可以有一个或者多个执行流(线程),但是主执行流只有一个(包含mian函数的线程)
因此之前所谈的进程是进程内部只有一个执行流的特殊情况(单线程进程),这里要清楚的认识到线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位
1.2 什么是线程
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位
在CPU的角度不区分进程的线程,统一视为:轻量级进程。CPU看到的都叫做执行流:task_struct 粒度
1.3 附言
操作系统学科中的线程概念只是一种指导思想,并不给定线程的具体实现方案
因此Linux系统内核中,线程的实现是用进程模拟的,复用了进程的代码和结构只是Linux这一款操作系统对线程具体实现方案,其他操作系统会有自己的具体实现方案,是会有差别的,比如Windows操作系统
Linux系统中不存在线程概念,只存在轻量级进程的概念------Linux系统特性
2 线程的优点
- 创建⼀个新线程的代价要⽐创建⼀个新进程⼩得多
- 线程与进程之间的切换相⽐,线程之间的切换需要操作系统做的⼯作要少很多
- 最主要的区别是线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的。这两种上下⽂切换的处理都是通过操作系统内核来完成的。内核的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出。
- 另外⼀个隐藏的损耗是上下⽂的切换会扰乱处理器的缓存机制。简单的说,⼀旦去切换上下⽂,处理器中所有已经缓存的内存地址⼀瞬间都作废了。还有⼀个显著的区别是当你改变虚拟内存空间的时候,处理的⻚表缓冲 TLB (快表)会被全部刷新,这将导致内存的访问在⼀段时间内相当的低效。但是在线程的切换中,不会出现这个问题,当然还有硬件cache。
- 线程占⽤的资源要⽐进程少很
- 能充分利⽤多处理器的可并⾏数量(多线程也具有)
- 在等待慢速I/O操作结束的同时,程序可执⾏其他的计算任务(多线程也具有)
- 计算密集型应⽤,为了能在多处理器系统上运⾏,将计算分解到多个线程中实现
- 在计算密集型应用中可以创建线程进行计算,但是不要太多。比如一台计算机有2个CPU 4核的处理器,建议最多创建8个线性进行计算------CPU个数 * CPU核数
- I/O密集型应⽤,为了提⾼性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
- 可以有多个
3 线程的缺点
- 不合理开辟线程的性能损失
- ⼀个很少被外部事件阻塞的计算密集型线程往往⽆法与其它线程共享同⼀个处理器。如果计算密集型线程的数量⽐可⽤的处理器多,那么可能会有较⼤的性能损失,这⾥的性能损失指的是增加了额外的同步和调度开销,⽽可⽤的资源不变。
- 健壮性降低
- 编写多线程需要更全⾯更深⼊的考虑,在⼀个多线程程序⾥,因时间分配上的细微偏差或者因共享了不该共享的变量⽽造成不良影响的可能性是很⼤的,换句话说线程之间是缺乏保护的。
- 缺乏访问控制
- 进程是访问控制的基本粒度,在⼀个线程中调⽤某些OS函数会对整个进程造成影响。
- 编程难度提⾼
- 编写与调试⼀个多线程程序⽐单线程程序困难得多
4 线程异常
- 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃
- 线程是进程的执⾏分⽀,线程出异常,就类似进程出异常,进⽽触发信号机制,终⽌进程,进程终⽌,该进程内的所有线程也就随即退出
5 线程用途
- 合理的使⽤多线程,能提⾼CPU密集型程序的执⾏效率
- 合理的使⽤多线程,能提⾼IO密集型程序的⽤⼾体验(如⽣活中我们⼀边写代码⼀边下载开发⼯具,就是多线程运⾏的⼀种表现)
- 需要并发处理任务(如Web服务器处理多个请求)
- 需要提高响应性(如GUI程序保持界面响应同时执行后台任务)
- 利用多核CPU提高计算性能
- I/O密集型任务(当一个线程等待I/O时其他线程可以继续执行)
6 进程与线程
- 进程具有独立性
- 线程共享虚拟地址空间和进程资源
- 进程是资源分配的基本单位
- 线程是调度的基本单位
- 线程共享进程数据,但也拥有⾃⼰的⼀部分"私有"数据
- 线程ID
- ⼀组寄存器
- 栈
- errno
- 信号屏蔽字
- 调度优先级
(1)进程的多个线程共享
同⼀地址空间,因此Text Segment(代码段)、Data Segment(数据段)都是共享的,如果定义⼀个函数,在各线程中都可以调⽤,如果定义⼀个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
- ⽂件描述符表
- 每种信号的处理⽅式(SIG_ IGN、SIG_ DFL或者⾃定义的信号处理函数)
- 当前⼯作⽬录
- ⽤⼾id和组id