了解线程
1.什么是线程
在传统的操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程
线程,其实就是一条流水线的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程
车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线
流水线的工作需要带能源,电源就相当于CPU
总而言之,进程只是用来把资源集中到一起(进程就是一个资源单位,或者说是资源集合),而线程才是cpu上的执行单位
多线程:就是在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间有多条流水线,都共用一个车间的资源
2.线程的创建开销小
创建进程的开销要远大于线程?
这个答案显然是否定的,就上述例子来说,你创建一个车间的时间就相当于创建一个进程的时间,而创建一个流水线的时间就好比创建一个线程。
要是创建一个车间(也就是进程),你需要去重新划分一块地(也就是重新分配内存空间),而创建一条流水线(也就是创建一个线程),是直
接再原来的车间里面直接创建(也就是在原来的进程中分配一块资源)
所以我们可以理解创建线程的时间要远远小于创建一个进程的时间
3.线程进程的区别
1.线程共享创建它的地址空间;进程有自己的地址空间
2.线程可以直接访问其进程的数据段;进程有自己的父进程的数据段副本
3.线程可以直接与进程的其他线程通信;进程必须使用进程间通讯与同级进程通信
4.新线程很容易创建;新进程需要复制父进程
5.线程可以对统一进程的线程进行很大的控制;进程只能对子进程进行控制
6.对主线程的更改(取消、优先级更改等)可能会影响进程的其他线程的行为;对父进程的更改不会影响子进程
4.为什么要使用多线程
多线程是指,在一个进程中开启多个线程,简单来讲;如果多个任务共用一块地址空间,那么必须在一个进程内开启多个线程,详细分为四点:
1.多线程共享一个进程的地址空间
2.线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程快很多倍,再有大量线程需要
动态修改时,这一特征很有用
3.若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的io处理,拥有多个线程允许这些活动
彼此重叠运行,从而加快程序执行的速度
4.再cpu系统中,为了最大限度利用多核,可以开启多个线程,比开进程开销要小的多(这一条并不适用于python)
5.多线程应用举例
开启一个字处理软件进程,该进程肯定需要办不止一件事情,比如监听键盘输入,处理文字,定时自动将文字保存到硬盘,这三个任务操作
的都是同一快数据,因而不能用多进程。只能在一个进程里并发的开启三个线程,如果时单线程,那就只能是,做一项任务时不能使用其他
功能
6.经典线程模型(了解)
- 多个线程共享一个进程的地址空间的资源,是对一台计算机上多个进程的模拟,又是也称线程为轻量级的进程
- 而对一台计算机上多个进程,则共享物理内存、磁盘、打印机等其他物理资源
- 多线程的运行也和多进程类似,是cpu在多个进程间的快速切换
- 不同点在于进程之间是相互竞争的,会出现争抢资源的情况。而同一个进程是由一个程序员的程序创建的,所以同一进程内的线程是合作关系,
一个线程可以访问另一个线程的内存地址,大家都是共享的
- 类似于进程,每个线程也有自己的堆线
- 不同于进程线程库无法利用时钟强制线程让出cpu,可以调用threading_yield运行线程自动放弃cpu,让另外一个线程运行
threading模块介绍
multiprocessing模块完全模仿了threading模块的接口,二者在使用层面,有很大的相似性。
1.开启线程的两种方式
方式一:
from threading import Thread
def foo():
print("这是一个新开子线程")
t = Thread(target=foo)
t.start()
print("这个在执行的是主线程")
方式二:
from threading import Thread
class My_foo(Thread):
def run(self):
print("这是一个新开子线程")
t = My_foo()
t.start()
print("这个在执行的是主线程")
2.在一个进程下开启多个线程与在一个进程下开启多个子进程的区别
1.开启线程的速度快
2.线程使用的是同一个pid,进程都是由不同的pid号
3.线程间数据共享,进程没有数据的共享,只能依靠进程间通讯
3.线程相关的其他用法
Thread实例对象的方法
- isAlive() 返回线程是否存活,(这个好像和 对象.is_alive() 方法没啥区别)
import time
from threading import Thread
def foo():
time.sleep(4)
print("1")
t = Thread(target=foo)
t.start()
time.sleep(6)
print(t.is_alive())
print(t.isAlive())
- getName():返回线程名 (这个好像也和 对象.name 方法没啥区别)
- setName():设置线程名 (这个好像也和 对象.name = "想设置的值" 方法没啥区别)
这两段代码就不写了,太low了,两个方法,就相当于换了个外套就出来溜达了
threading模块提供的一些方法:
- threading.currentThread():返回当前的线程变量
- 打印的类似这种:<_MainThread(MainThread, started 8084)> (这个是主线程)
<Thread(Thread-1, started 11412)> (这个是子线程)
- threading.enumerate():返回一个包含正在运行的线程list。正在运行指进程启动后、结束前,不包括启动前和终止后的线程。
- threading.activeCount():返回正在运行的线程数量 (其实就是把enumerate()中的正在运行的线程数量统计了一下)
4.守护线程
- 和守护进程的定义方法是一样的,无论是进程还是线程,都遵循:守护线程/进程会等待主线程/进程运行完毕后被销毁
- 需要强调的是运行完毕并非终止运行
- 对于主进程来说:运行完毕是指主进程代码运行完毕
- 对于主线程来说:运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
详解:
- 主进程 在其代码结束后就已经运行完毕了(守护进程在此时就会被回收),然后主进程会一直等非守护进程的子进程都运行完毕后
回收子进程的资源,如果不这样设计就会产生僵尸进程,才会结束
- 主线程 在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就会被回收)。因为主线程的结束意味着进程的结束,进程整体
的资源都会被回收,而进程必须保证非守护线程都运行完毕后才能结束