一,线程 ====> 线程由代码段,数据段,TCB段组成
线程被称作轻量级的进程
GIL:全局解释锁(只有Cpython解释器才有)对于线程来说,同一时间只允许一个线程访问cpu,因为有了GIL,所以没有真正的并行
进程是资源分配的基本单位,线程是计算机可执行基本单位
线程不可以自己独立拥有资源,线程的执行,必须依赖于所属进程中的资源
进程中必须至少应该有一个线程
线程分用户级线程和内核级线程:
用户级线程:对于程序员来说的,这样的线程完全被程序员控制执行调度
内核级线程:对于计算机内核来说的,这样的线程完全被内核控制调度。
二, 进程与线程的区别
(1)cpu切换进程要比cpu切换线程 慢很多,在python中,如果IO操作过多的话,使用多线程最好了
(2)在同一个进程内,所有线程共享这个进程的pid,
也就是说所有线程共享所属进程的所有资源(包括进程中的全局变量)和内存地址
(3)因为有GIL锁的存在,在Cpython中,没有真正的线程并行。但是有真正的多进程并行,当你的任务是计算密集的情况下,使用多进程好
总结:在CPython中,IO密集用多线程,计算密集用多进程
(4)关于守护线程和守护进程的事情(注意:代码执行结束并不代表程序结束)
守护进程:要么自己正常结束,要么根据父进程的代码执行结束而结束 ======> 注意:父进程的代码执行完毕,守护进程就不执行
守护线程:要么自己正常结束,要么根据父线程的执行结束而结束 ======> 注意:主线程的执行结束(包含普通线程执行结束),守护线程也结束
三, 线程的使用方法
(1)递归锁用来解死锁,在单个线程内每获取一次锁会加计数一次,,释放一次会减计数一次,为零时就释放完毕
from threading import RLock import time def wc(): r1.acquire() # 小明获得厕所资源 注意这个锁是个整体,里面如果有小锁 time.sleep(1) #那么小锁用完必须释放在大锁内 print('小明在厕所解手') r2.acquire() #小明拿到纸 print('小明拿到卫生纸了') r2.release() #小明释放纸 r1.release() # 小明释放厕所资源 def pap(): r2.acquire() time.sleep(1) print('小雪在厕所解手') r1.acquire() print('小雪拿到卫生纸了') r1.release() r2.release() if __name__ == '__main__': r1 = r2 = RLock() #配了两把锁 p1 = Thread(target=wc,args=()) #开启一个上厕所的线程 p2 = Thread(target=pap,args=()) #开启一个纸的线程 p1.start() p2.start()
(2)定时器(定时去执行任务)
from threading import Timer# 定时器 def func(): print('就是这么nb!') Timer(2.5,func).start() # Timer(time,func) # time:睡眠的时间,以秒为单位 # func:睡眠时间之后,需要执行的任务
(3)信号量(一次性执行几个线程)
from threading import Semaphore,Thread import time def func(sem,i): sem.acquire() print('第%s个人进入屋子'%i) time.sleep(2) print('第%s个人离开屋子'%i) sem.release() sem = Semaphore(20) for i in range(20): t = Thread(target=func,args=(sem,i)) t.start()
(4)事件
from threading import Thread,Event import time,random def conn_mysql(e,i): count = 1 #计数 while count <= 3: if e.is_set(): #接收e.set()的值,如果事件是真则执行 print('第%s个人连接成功!'%i) break print('正在尝试第%s次重新连接...'%(count)) e.wait(0.5) count += 1 def check_mysql(e): print('