python 线程
一. 为什么要有线程
进程是资源分配的最小单位,线程是CPU调度的最小单位.
进程虽然可以提高计算机的利用率,但是进程还是有很多缺点,如果想同时做多件事,需要多进程;进程在执行的过程中,如果被阻塞,例如等待输入,整个进程就会被挂起,即使进程中有些工作不依赖于输入的数据,也无法执行.
二. 线程和进程的关系
进程间的内存空间和资源时相互独立的,同一个进程内的线程是可以共享进程内的所有资源.某进程的线程在其他进程是不可见的.
进程间的通信靠IPC(队列,管道),线程间可以直接读写进程的数据段
线程上下文切换比进程的上下文切换要快的多.
在多线程操作系统中,进程不是一个可以执行的实体.
三. 线程的特点
线程的实体基本不拥有系统的资源,除了必不可少的要保证独立运行的资源
线程的实体由代码块,数据块,TCP组成.线程是动态概念
在多线程os中,线程是能独立运行的基本单位,因此也是独立调度和独立分派的基本单位.线程的切换是非常迅速的而且开销很小
线程共享进程内的所有资源,所有线程都具有相同的进程的id
在同一个或不同进程中的线程是可以并发执行的.
线程分为 :
用户级线程 : 内核的切换由用户(程序员)控制内核切换,不需要内核干涉,减少进出内核的消耗,但不能很好的利用多核CPU.
内核级线程 : 线程切换由内核控制,当线程进行切换的时候,由用户态转化为内核态.线程切换完成后要从内核态返回用户态,可以很好的利用多核的CPU.
四. GIL(全局解释器锁)
将线程上锁,是CPython解释器上的一个锁,意思是在同一时间值允许一个线程访问CPU.
因为GIL锁的存在,在CPython中,没有真正的线程并行. 但是有真正的 多进程并行.
总结 : 在Cpython中, IO密集用多线程,计算密集用多进程.
五. python线程中的模块
1. threading模块 : 和进程中multiprocessing模块使用方法有很大的相似性
2. Thread : 创建线程需要导入的模块 ( from threading import Thread )
3. 线程的创建 : (普通创建和继承Thread类)
t = Thread( target = , args = ( '元组', ))
t.start()
class Xiancheng(Thread):
def __init__(self):
super().__init__()
4. 线程的其他方法 :
相对于实例化对象的方法 :
isAlive() : 判断线程是否存活
getName() : 返回线程名
setName() : 设置线程名
threading模块的一些方法 :
threading.currentThread() : 返回当前的线程变量
threading.enumerate() : 返回一个包含正在运行线程的列表. 正在运行指启动后,结束前,不包括启动前和终止后的线程.
threading.activeCount() : 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果.
5. 守护线程 :
守护进程,根据父进程的代码结束而结束.
守护线程,根据父进程的执行结束而结束.
6. 锁
死锁是指两个或两个以上的进程或者线程在争夺资源的时候,发生永久等待的现象,即A/B进程或线程等待B/A进程或线程归还某一个A/B必须要使用的资源.这样的互相等待就是所谓的死锁.
递归锁 : 递归锁将某一个执行进程或线程需要的资源进行递归上锁,只要拿到必须资源中的其中一个资源,其他资源也相当于拿到,其他进程/线程不可以在去拿,只能等拿到资源的进程/线程将资源释放后才能继续使用资源.
#############使用筷子吃饭的死锁例子############# from threading import Thread,Lock def func(l1,l2):#a如果想要吃饭,就要有饭和筷子两种资源 l1.acquire()#拿到饭的资源并且上锁 print('a拿到饭了')#如果这个时候a拿到饭的资源.但是筷子的资源被b抢占并且上了锁,a就处于等待b将筷子资源释放的阶段. l2.acquire() print('a拿到筷子了') print('a把饭吃了') l2.release() l1.release() def func1(l2,l1): l2.acquire()#当a将饭的资源上锁之后,b有可能会抢到筷子的资源 print('b拿到筷子了')#由于a已经将饭资源上了锁,所以b处于等待a将饭资源释放的阶段,而a也在等待b释放资源,所有二者相互等待就形成了死锁 l1.acquire() print('b拿到饭了') print('b把饭吃了') l1.release() l2.release() if __name__ == '__main__': l1 = Lock()#饭的锁 l2 = Lock()#筷子的锁 t = Thread(target=func,args=(l1,l2))#每个方法只有得到饭和筷子两种资源才能将方法执行完毕并且释放资源 t1 = Thread(target=func1,args=(l2,l1)) t.start()#两种方法几乎同时开启,开启后无法控制. t1.start()
#################递归锁解决死锁################# from threading import Thread,RLock def func(l1,l2): l1.acquire() print('a拿到饭了') l2.acquire() print('a拿到筷子了') print('a把饭吃了') l2.release() l1.release() def func1(l1,l2): l2.acquire() print('b拿到筷子了') l1.acquire() print('b拿到饭了') print('b把饭吃了') l1.release() l2.release() if __name__ == '__main__': f_lock=k_lock=RLock() t = Thread(target=func,args=(f_lock,k_lock)) t1 = Thread(target=func1,args=(f_lock,k_lock)) t.start() t1.start()
7. 信号量
同进程的一样
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()
from threading import Thread,Semaphore import time def func(i,s): s.acquire()#将执行的线程加锁,最大为4个线程 print('%s 进入了ktv'% i) time.sleep(1) s.release()#将执行完的线程解锁 if __name__ == '__main__': s = Semaphore(4)#设置最大可以同时执行的线程数为4 for i in range(10): t = Thread(target=func,args=(i,s)) t.start()
8. 事件
同进程的一样
线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在初始情况下,Event对象中的信号标志被设置为False。如果有线程等待一个Event对象, 而这个Event对象的标志为False,那么这个线程将会被一直阻塞直至该标志为True。一个线程如果将一个Event对象的信号标志设置为True,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为True的Event对象,那么它将忽略这个事件, 继续执行
9. 条件
条件的目的是使线程等待,只能满足某一个条件后,才会释放线程(释放线程数根据notify传的参数释放)
方法 : acquire() ; release() ; wait()( 阻塞,等待notify发送信号,唤醒线程) ; notify()
from threading import Thread,Condition def func(con,i): con.acquire() con.wait()# 线程执行到这里,会阻塞住,等待notify发送信号,来唤醒此线程 con.release() print('第%s个线程开始执行了!'%i) if __name__ == '__main__': con = Condition() for i in range(10): t = Thread(target=func,args=(con,i)) t.start() while 1: num = int(input(">>>")) con.acquire() con.notify(num)# 发送一个信号给num个正在阻塞在wait的线程,让这些线程正常执行 con.release()
10. 定时器
引入定时器 : from threading import Timer
定时器参数 : Timer( time , func ) ,time : (睡眠时间,以秒为单位) func : (睡眠时间过后,需要执行的任务)
from threading import Timer def hello(): print("hello, world") t = Timer(1, hello)#1秒之后执行hello方法 t.start()