线程
线程被称作轻量级的进程。 GIL:全局解释锁(只有Cpython解释器才有)
对于线程来说,因为有了GIL,所以没有真正的并行
计算机的执行单位以线程为单位。计算机的最小可执行是线程。
进程是资源分配的基本单位。线程是可执行的基本单位,是可被调度的基本单位。
线程不可以自己独立拥有资源。线程的执行,必须依赖于所属进程中的资源。
进程中必须至少应该有一个线程。
线程又分为用户级线程和内核级线程(了解)
用户级线程:对于程序员来说的,这样的线程完全被程序员控制执行,调度
内核级线程:对于计算机内核来说的,这样的线程完全被内核控制调度。
进程由 代码段 数据段 PCB组成(process control block)
线程由 代码段 数据段 TCB组成(thread control block)
线程和进程的比较
thread - 线程
import thread 操作线程的模块
import threading 用这个去操作线程
(1) cpu切换进程要比cpu切换线程 慢很多
在python中,如果IO操作过多的话,使用多线程最好了
(2) 在同一个进程内,所有线程共享这个进程的pid,也就是说所有线程共享所属进程的所有资源和内存地址
(3) 在同一个进程内,所有线程共享该进程中的全局变量
(4) 因为有GIL锁的存在,在Cpython中,没有真正的线程并行。但是有真正的多进程并行
当你的任务是计算密集的情况下,使用多进程好
总结:在CPython中,IO密集用多线程,计算密集用多进程
(5)关于守护线程和守护进程的事情(注意:代码执行结束并不代表程序结束)
守护进程:要么自己正常结束,要么根据父进程的代码执行结束而结束
守护线程:要么自己正常结束,要么根据父线程的执行结束而结束
线程的特点
TCB包括以下信息: (1)线程状态。 (2)当线程不运行时,被保存的现场资源。 (3)一组执行堆栈。 (4)存放每个线程的局部变量主存区。 (5)访问同一个进程中的主存和其它资源。 用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。
python和线程
理论知识
全局解释器锁GIL
Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
在多线程环境中,Python 虚拟机按以下方式执行:
a、设置 GIL;
b、切换到一个线程去运行;
c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
d、把线程设置为睡眠状态;
e、解锁 GIL;
d、再次重复以上所有步骤。
在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。
python线程模块的选择
Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
避免使用thread模块,因为更高级别的threading模块更为先进,对线程的支持更为完善,而且使用thread模块里的属性有可能会与threading出现冲突;其次低级别的thread模块的同步原语很少(实际上只有一个),而threading模块则有很多;再者,thread模块中当主线程结束时,所有的线程都会被强制结束掉,没有警告也不会有正常的清除工作,至少threading模块能确保重要的子线程退出后进程才退出。
thread模块不支持守护线程,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。
threading模块
multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍(官方链接)
需注意,玩多线程编程时,可以不用再像写多进程一样,每个py文件中必须加上if __name__ == '__main__'这一行代码了
线程的创建Threading.Thread类
from threading import Thread def func(): print("我是线程") t =Thread(target=func) t.start()
自定义类开启线程 from threading import Thread class MyTheard(Thread): def __init__(self): super(MyTheard, self).__init__() def run(self): print("我是线程") t = MyTheard() t.start()
主进程会等线程执行完后结束 from threading import Thread import time def func(): print("我是线程") time.sleep(2) t = Thread(target=func) t.start()
多线程与多进程
from threading import Thread from multiprocessing import Process import os import time def son(n): print('%s中,我是儿子,我的pid号是: %s'%(n,os.getpid())) if __name__ == '__main__': t = Thread(target=son,args=('线程',)) t.start() print('线程中,我是爸爸,我的pid号是:%s'%os.getpid()) time.sleep(2) p = Process(target=son,args=('进程',)) p.start() print('进程中,我是爸爸,我的pid号是:%s' % os.getpid())
线程与进程开启的效率对比
from threading import Thread from multiprocessing import Process import time def func(): pass if __name__ == '__main__': start = time.time() for i in range(100): t = Thread(target=func) t.start() print(time.time()-start) start = time.time() for i in range(100): p = Process(target=func) p.start() print(time.time() - start)
共享数据的比较
from threading import Thread import time def func(): global num num-=1 time.sleep(1) if __name__ == '__main__': num = 100 t = Thread(target=func,) t.start() t.join() print(num) # 结论:很明显,多线程是一个共享一个进程内部的全局变量
线程---信号量
from threading import Thread,Semaphore import time def func(s,i): s.acquire() print("第%s个人进屋子" %i) time.sleep(2) print("第%s个人出屋子" %i) s.release() if __name__ == '__main__': s = Semaphore(20) for i in range(20): t = Thread(target=func,args=(s,i)) t.start()
线程----互斥锁
死锁演示
from threading import Thread,Lock from multiprocessing import Process import time def man(l_paper,l_toilet): l_toilet.acquire() print("小明在厕所上厕所") time.sleep(1) l_paper.acquire()
print("小明拿到卫生纸了") time.sleep(0.5) print("小明上完厕所了") l_toilet.release() l_toilet.release() def women(l_paper,l_toilet): l_paper.acquire() print("小红拿到了卫生纸") time.sleep(1) l_toilet.acquire() print("小红在厕所上厕所") time.sleep(0.5) print("小红上完厕所了") l_toilet.release() l_toilet.release() if __name__ == '__main__': l_paper = Lock() l_toilet = Lock() t_man = Thread(target=man,args=(l_paper,l_toilet)) t_women = Thread(target=women,args=(l_paper,l_toilet)) t_man.start() t_women.start()
解决死锁
from threading import Thread,RLock from multiprocessing import Process import time def man(l_paper,l_toilet): l_toilet.acquire() print("小明在厕所上厕所") time.sleep(1) l_paper.acquire() print("小明拿到卫生纸了") time.sleep(0.5) print("小明上完厕所了") l_toilet.release() l_toilet.release() def women(l_paper,l_toilet): l_paper.acquire() print("小红拿到了卫生纸") time.sleep(1) l_toilet.acquire() print("小红在厕所上厕所") time.sleep(0.5) print("小红上完厕所了") l_toilet.release() l_toilet.release() if __name__ == '__main__': l_paper = l_toilet = RLock() t_man = Thread(target=man,args=(l_paper,l_toilet)) t_women = Thread(target=women,args=(l_paper,l_toilet)) t_man.start() t_women.start()
递归锁(万能钥匙)
from threading import RLock r = RLock() r.acquire() r.acquire() r.acquire() r.acquire() r.acquire() r.acquire() r.acquire() r.acquire() print(123456789)
定时器
Timer(time,function)
time:睡眠时间,以秒为单位
function:睡眠时间之后需要执行的任务
from threading import Timer def func(): print("就是这么帅") Timer(3,func).start()
线程-----事件
from threading import Thread,Event import time,random def connect(e,i): count = 1 while count <= 3: if e.is_set(): print("第%s个人连接成功." % i) break print("正在尝试第%s次连接......" %(count)) e.wait(0.5) count+=1 def update(e): print("\033[32m 数据库正在更新中 \033[0m") time.sleep(random.randint(1,2)) e.set() if __name__ == '__main__': e =Event() t_update = Thread(target=update,args=(e,)) t_update.start() for i in range(10): t_connect = Thread(target=connect,args=(e,i)) t_connect.start()
使用条件机制调度线程
Condition涉及4个方法
acquire()
release()
wait()
notify(int)是指给wait发一个信号,让wait变成不阻塞
int是指,发多少个信号给wait
from threading import Thread,Condition def func(c,i): c.acquire() c.wait() c.release() print("第%s个线程开始执行了." %i) if __name__ == '__main__': c = Condition() for i in range(10): t = Thread(target=func,args = (c,i)) t.start() while 1: num = int(input(">>>")) c.acquire() c.notify(num) c.release()
守护线程
def func(): time.sleep(2) print(123) def func1(): time.sleep(5) print(456789) if __name__ == '__main__': t = Thread(target=func,) t.daemon = True t.start() t1 = Thread(target=func1,) t1.start() print(666666)