一、多线程
1.什么是线程
线程指的是一条流水线的工作过程
进程根本就不是一个执行单位,进程其实是一个资源单位
一个进程内自带一个线程,线程才是执行单位
2.进程VS线程
1.同一进程内线程们共享该进程内资源,不同进程内的线程资源肯定是隔离
2.创建线程的开销比创建进程要小的多
二、开启线程的两种方式:
方法一: from threading import Thread import time def task(name): print('%s is running' %name) time.slepp(3) if __name__=='__main__': t=Thread(target=task,args=('egon',)) t.start() print('主线程') 代码结果: egon is running 主线程 方法二: from threading import Thread import time class MyThread(Thread): def run(self): print('%s is running' %self.name) time.sleep(3) if __name__=='__main__': t=MyThread() t.start() print('主线程') 代码结果: Thread-1 is running 主线程
三、多线程与多进程的区别
1、在主进程下开启多个线程,每个线程的都跟主进程的pid一样。
而在主进程下开多个子进程,每个子进程都有不同的pid
2、开启速度:
1.在主进程下开启线程:几乎是t.start()的同时就将线程开启了,线程创建开销小
2.在主进程下开启子进程:p.start()将开启进程的信号发给操作系统后,操作系统要申请内存空间,然后拷贝父进程地址空间到子进程,开销远大于线程。
from threading import Thread from multiprocessing import Process import os def work(): print('hello',os.getpid()) if __name__ == '__main__': #part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样 t1=Thread(target=work) t2=Thread(target=work) t1.start() t2.start() print('主线程/主进程pid',os.getpid()) #part2:开多个进程,每个进程都有不同的pid p1=Process(target=work) p2=Process(target=work) p1.start() p2.start() print('主线程/主进程pid',os.getpid()) 代码运行结果: hello 4624 hello 4624 主线程/主进程pid 4624 主线程/主进程pid 4624 hello 6324 hello 6136
3、同一进程内的多个线程共享该进程内的资源
from threading import Thread x=1000 def task(): global x x=0 if __name__=='__main__': t=Thread(target=task,) t.start() t.join() print('主线程',x) 打印结果: 主线程 0
四、线程对象的其他方法
is_alive():返回线程是否活动的 current_thread():返回当前的线程变量 enumerate():返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程 active_count():返回正在运行的线程数量 from threading import Thread,current_thread,active_count,enumerate import time def task(): print('%s is running' %current_thread().name) time.sleep(3) if __name__ == '__main__': t1=Thread(target=task,) t2=Thread(target=task,) t3=Thread(target=task,) t1.start() t2.start() t3.start() print(t1.is_alive()) print(active_count()) print(enumerate()) print('主线程',current_thread().name) 代码运行结果: 第一个线程 is running Thread-1 is running Thread-2 is running True 4 [<_MainThread(MainThread, started 6900)>, <Thread(第一个线程, started 3996)>, <Thread(Thread-1, started 4980)>, <Thread(Thread-2, started 6376)>] 主线程 MainThread
五、守护线程
无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁
需要强调的是:运行完毕并非终止运行
1、对于主进程来说:主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束。
2、对于主线程来说:主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
from threading import Thread import time def foo(): print(123) time.sleep(5) print('end123') def bar(): print(456) time.sleep(3) print('end456') if __name__=='__main__': t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print('main---------') 代码运行结果: 123 456 main------- end456
六、线程互斥锁
from threading import Thread,Lock import time mutex=Lock() x=100 def task(): global x mutex.acquire() temp=x time.sleep(0.1) x=temp-1 mutex.release() if __name__=='__main__': satrt=time.time() t_1=[] for i in range(100): t=Thread(target=task) t_1.append(t) t.start() for t in t_1: t.join() print('主',x) print(time.time()-start) 执行结果: 主 0 10.00457239151001
七、死锁现象与递归锁
死锁:是指两个或者两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如无外力作用,它们将无法推进下去,此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
from threading import Thread,Lock import time mutexA=Lock() mutexB=Lock() class MyThread(Thread): def run(self): self.f1() self.f2() def f1(self): mutexA.acquire() print('%s 拿到了A锁' %self.name) mutexB.acquire() print('%s 拿到了B锁' %self.name) mutexB.release() mutexA.release() def f2(self): mutexB.acquire() print('%s 拿到了B锁' %self.name) time.sleep(0.1) mutexA.acquire() print('%s 拿到了A锁' %self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(10): t=MyThread() t.start() 代码执行结果: Thread-1 拿到了A锁 Thread-1 拿到了B锁 Thread-1 拿到了B锁 Thread-2 拿到了A锁 主
递归锁:在python中支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁,二者的区别是:递归锁可以连续acquire多次,而互斥锁只能acquire一次。
from threading import Thread,Lock,RLock import time mutexA=mutxeB=RLock() class MyThread(Thread): def run(self): self.f1() self.f2() def f1(self): mutexA.acquire() print('%s 拿到了A锁' %self.name) mutexB.acquire() print('%s 拿到了B锁' %self.name) mutexB.release() mutexA.release() def f2(self): mutexB.acquire() print('%s 拿到了B锁' %self.name) time.sleep(0.1) mutexA.acquire() print('%s 拿到了A锁' %self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(5): t=MyThread() t.start() 代码执行结果:
Thread-1 拿到了A锁
Thread-1 拿到了B锁
Thread-1 拿到了B锁
主
Thread-1 拿到了A锁
Thread-2 拿到了A锁
Thread-2 拿到了B锁
Thread-2 拿到了B锁
Thread-2 拿到了A锁
Thread-4 拿到了A锁
Thread-4 拿到了B锁
Thread-4 拿到了B锁
Thread-4 拿到了A锁
Thread-3 拿到了A锁
Thread-3 拿到了B锁
Thread-3 拿到了B锁
Thread-3 拿到了A锁
Thread-5 拿到了A锁
Thread-5 拿到了B锁
Thread-5 拿到了B锁
Thread-5 拿到了A锁
八、信号量
信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行。
from threading import Thread,Semaphore,current_thread import time,random sm=Semaphore(5) def go_wc(): sm.acquire() print('%s ======>>>ing' %current_thread().getName()) time.sleep(random.randint(1,3)) sm.release() if __name__ == '__main__': for i in range(17): t=Thread(target=go_wc) t.start() 代码运行结果: Thread-1 ======>>>ing Thread-2 ======>>>ing Thread-3 ======>>>ing Thread-4 ======>>>ing Thread-5 ======>>>ing Thread-6 ======>>>ing Thread-7 ======>>>ing Thread-8 ======>>>ing Thread-9 ======>>>ing Thread-10 ======>>>ing Thread-11 ======>>>ing Thread-12 ======>>>ing Thread-13 ======>>>ing Thread-15 ======>>>ing Thread-14 ======>>>ing Thread-16 ======>>>ing Thread-17 ======>>>ing