线程
线程
一、什么是线程
二、怎么创建线程
三、线程与进程的关系 ——重点
四、线程的作用与如何使用线程 ——重点
五、线程的特点 ——重点
六、守护线程daemon
七、线程 Thread 中的三把锁(Lock,Rlock,Semaphore)
八、Thread 类的常用属性
JoinableQueue 列队
join:等待某个任务的完成;able: ......的能力; Queue:列队
——可以被 join 的列队
p = JoinableQueue()
p.put() ——放入值
p.get() ——取出值
p.task_done ——告诉这个列队数据已经被处理完了
task_done 的次数应该等于 put 的调用次数
p.join ——等待列表中的数据被处理完毕
一、什么是线程:
线程是操作系统最小的运算调度单位,是独立调度和分派的基本单位,被包含在进程中,是进程中的实际运作单位,一条线程指的是进程中的一个单一顺序的控制流
二、怎么创建线程:
1、实例化Thread类,target参数用于指定子线程要执行的任务
2、继承Thread类,覆盖run方法
三、线程与进程的关系 ——重点
线程不能单独存在 必须存在于进程中,
进程是一个资源单位,其包含了运行程序所需的所有资源
,没有线程,进程中的资源无法被利用起来,所以一个进程至少包含一个线程,称之为主线程
当我们启动一个程序时,操作系统就会为这个程序创建一个主线程,线程可以由程序后期开启 ,自己开启线程称之为子线程
四、进程的作用与如何使用线程 ——重点
1、进程的作用:
目的只有一个就是为了 提高效率,就像一个车间 如果产量更不上 就在造一条流水线,,所以通常情况是创建新的流水线 而不是建造新的车间 即 一般创建新的线程,而不是创建新的进程
2、如何使用线程
线程使用方法和多进程一模一样,没有任何的区别
不过开启线程的代码可以放在任何位置,而开启进程必须放在自运行判断下面
之所以使用方法完全相同是因为,多进程其实是为了弥补多线程的缺憾而诞生的。详见GIL锁
五、线程的特点 ——重点
1、创建开销小(轻型实体)
2、同一个进程中的多个线程之间的数据时共享的(共享线程资源)
六、守护线程 daemon
一个线程可以设置为另一个线程的守护线程
特点: 被守护线程结束后守护线程也随之结束
守护线程会等到所有非守护线程结束后结束,前提是除了主线程之外 还有后别的非守护,如果守护线程已经完成任务立刻就结束了
七、线程 Thread 中的 锁
1、互斥锁 Lock
共享意味着资源的竞争,线程中也存在安全问题,
多线程可以并发执行,一旦并发了并且访问了同一个资源就会有问题
解决方案: 加 互斥锁
2、死锁问题
当程序出现了不止一把锁,分别被不同的线程持有, 现有一个资源 要想使用这个资源,就必须同时具备两把锁,这时候程序就会进程无限卡死状态 ,这就称之为死锁
Eg:
规定:想要吃饭 必须具备盘子和筷子,但是一个人拿着盘子,等筷子;另一个人拿着筷子,等盘子。两个人都无法吃到饭。
如何避免死锁问题;
1)锁不要有多个,尽量只用一个
2)如果真的发生了死锁问题,必须迫使其中一方先交出锁
3、可重入锁(递归锁) Rlock
Rlock 称之为递归锁或者可重入锁,Rlock不是用来解决死锁问题的
Rlock 与 Lock唯一的区别:
Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次 如果一个线程已经执行过acquire 其他线程将无法执行acquire
4、信号量 Semaphore
被 Semaphore(i) 锁定的代码 同时可以被 i 个线程并发访问
Lock 锁住的程序同时只能由一个线程访问
Semaphore(i) 锁住的程序可以同时被一个或者多个线程访问, i 指的是可以同时被访问的个数
用途: 仅用于控制并发访问,并不能防止并发访问时修改数据再写入时造成的问题
八、Thread 类的常用属性:
threading 模块包含的常用方法
import threading
print(threading.current_thread().name) —— 获取当前线程对象的名字
print(threading.active_count()) —— 获取目前活跃的线程数量
print(threading.enumerate()) —— 获取所有线程对象
实例化 Thread
t = Thread(name="aaa")
t.join() —— 主线程等待子线程执行完毕
print(t.name) —— 线程名称
print(t.is_alive()) —— 是否存活
print(t.isdaemon()) —— 是否为守护线程

浙公网安备 33010602011771号