一,线程 ====> 线程由代码段,数据段,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('\033[42m 数据库正在维护 \033[0m') time.sleep(random.randint(1,2)) e.set() if __name__ == '__main__': e = Event() #创建一个时间对象 t_check = Thread(target=check_mysql,args=(e,)) #注意:将时间对象传给执行函数 t_check.start() #起动执行检查数据库的任务 for i in range(10): t_conn = Thread(target=conn_mysql,args=(e,i)) #注意:将时间对象传给执行函数 t_conn.start() #启动执行10个线程对象去执行连接数据库任务
(5)条件(可以控制线程的多少,线程数执行完毕就结束)
from threading import Thread,Condition import time # Condition涉及4个方法 # acquire() # release() # wait() 是指让线程阻塞住 # notify(int) 是指给wait发一个信号,让wait变成不阻塞 # int是指,你要给多少给wait发信号 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: con.acquire() num = int(input(">>>")) con.notify(num)# 发送一个信号给num个正在阻塞在wait的线程,让这些线程正常执行 con.release()
四,,线程池
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor #不管是ProcessPoolExecutor的进程池 还是Pool的进程池,回调函数都是父进程调用的。 # concurrent.futures 这个模块是异步调用的机制 # concurrent.futures 提交任务都是用submit # for + submit 多个任务的提交 # shutdown 是等效于Pool中的close+join,是指不允许再继续向池中增加任务,然后让父进程(线程)等待池中所有进程执行完所有任务。 #进程池和线程池不需要用启动start def func1(num): sum = 0 for i in range(num): sum += i ** 2 return sum def back_func1(res): # 回调函数对数据二次处理,一般会写入文件截取处理后的数据,无法return返回值 with open('aa','a',encoding='utf-8') as f1: f1.write(str(66 * (res.result()))+'\n') #一,关于线程的使用事项方法 if __name__ == '__main__': #(1)用map函数创建多个线程 t = ThreadPoolExecutor(20) lis = [] ret = map(func1,range(200))#,开启多个线程:相当于for循环加上submit,,返回的是个生成器,用for循环去取值 for i in ret: lis.append(i) print(len(lis),lis) #(2)用submit函数创建多个线程 t1 = ThreadPoolExecutor(20) for i in range(200): t1.submit(func1,i).add_done_callback(back_func1) #异步时,回调函数被主线程 # ret = t.submit(func1,i) #开启一个线程第一个参数是函数,第二个是参数(是个元组类型,args = (i,))可以简写 # print(ret.result()) #注意返回值:用ret.result()来取值 t1.shutdown() #这个命令的功能相当于t.close() 和t.join() 的功能,等待子线程执行结束 #二,关于进程的使用事项方法 #(1)用map函数创建多个进程 if __name__ == '__main__': #(1) 用sumit函数创建多个进程: p = ProcessPoolExecutor(5) for i in range(100): # p.submit(func1,i).add_done_callback() #异步时,回调函数被主进程调用 ret = p.submit(func1, args = (i,)) ##开启一个进程第一个参数是函数,第二个是参数(是个元组类型,args = (i,))可以简写 print(ret.result()) #注意返回值:用ret.result()来取值 p.shutdown() # 这个命令的功能相当于t.close() 和t.join() 的功能,等待子进程执行结束 #(2)用map函数创建多个进程: p1 = ProcessPoolExecutor(20) lis = [] ret = map(func1,range(200))#,开启多个进程:相当于for循环加上submit,,返回的是个生成器,用for循环去取值 for i in ret: lis.append(i) #这个操作也相当于等待所有的任务执行结束 相当于执行t.shutdown() print(len(lis),lis)