一、多线程与多进程的效率对比 (1)多线程 from threading import Thread,current_thread # 多线程 current_thread 当前线程 import time def run(msg): print("这个是独立线程运行的代码",msg) time.sleep(1) if __name__ == '__main__': new_time = time.time() print("++++++++++ main start +++++++++++") for x in range(10): t1 = Thread(target=run,args=("这个是参数%s"%x,)) t1.start() print("========== main end ==========", current_thread().getName(),time.time()-new_time) # current_thread().getName()可以得到当前主线程的名称 ''' 运行结果: ++++++++++ main start +++++++++++ 这个是独立线程运行的代码 这个是参数0 这个是独立线程运行的代码 这个是参数1 这个是独立线程运行的代码 这个是参数2 这个是独立线程运行的代码 这个是参数3 这个是独立线程运行的代码 这个是参数4 这个是独立线程运行的代码 这个是参数5 这个是独立线程运行的代码 这个是参数6 这个是独立线程运行的代码 这个是参数7 这个是独立线程运行的代码 这个是参数8 这个是独立线程运行的代码 这个是参数9 ========== main end ========== MainThread,0.0010006427764892578 ''' (2)多进程 from multiprocessing import Process # 多进程 import time def run(msg): print("这个是独立线程运行的代码",msg) time.sleep(1) if __name__ == '__main__': new_time = time.time() print("++++++++++ main start +++++++++++") for x in range(10): t1 = Process(target=run,args=("这个是参数%s"%x,)) t1.start() print("========== main end ==========",time.time()-new_time) ''' 运行结果: ++++++++++ main start +++++++++++ ========== main end ========== 0.2971487045288086 这个是独立线程运行的代码 这个是参数0 这个是独立线程运行的代码 这个是参数2 这个是独立线程运行的代码 这个是参数1 这个是独立线程运行的代码 这个是参数9 这个是独立线程运行的代码 这个是参数3 这个是独立线程运行的代码 这个是参数5 这个是独立线程运行的代码 这个是参数8 这个是独立线程运行的代码 这个是参数4 这个是独立线程运行的代码 这个是参数7 这个是独立线程运行的代码 这个是参数6 ''' 二、多线程,类的实现方式
from threading import Thread class Team(Thread): def __init__(self, name): super().__init__(name=name) # 继承自Thread def run(self): for i in range(3): print("这是类中的子线程") if __name__ == '__main__': print("+++++++++++ main start ++++++++++++") t = Team("丽丽") t.start() print("========== main end ============") ''' 运行结果: +++++++++++ main start ++++++++++++ 这是类中的子线程 # 可以看出多线程的运行速度几乎超过了主线程,这在多进程中是不会出现的 ========== main end ============ 这是类中的子线程 这是类中的子线程 这是类中的子线程 ''' 三、多线程共享全局变量
我们前面已经说过了,多进程之间的数据时独立的,各自都有一份,即便是全局变量也不共享,那么一个进程中的多线程之间的全局变量呢?注意:多线程之间的全局数据数是共享的,因为多线程是在一个进程中,数据是互相可以访问的,案例如下:
import threading import time num = 100 def task1(): global num for x in range(3): num += 1 print("run1中的num=",num) def task2(): print("run2中的num=",num) def run(): t1 = threading.Thread(target=task1) t1.start() time.sleep(1) t2 = threading.Thread(target=task2) t2.start() if __name__ == '__main__': run() |
由此可知,一个进程间的多线程(下面开始统一叫多线程,因为我们一直说的多线程就是一个进程下的多线程)的全局数据是共享的。
四、互斥锁(metux)当多个线程⼏乎同时修改某⼀个共享数据的时候,需要进⾏同步控制线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引⼊互斥锁。
互斥锁为资源引⼊⼀个状态:锁定/⾮锁定。
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“⾮锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有⼀个线程进⾏写⼊操作,从⽽保证了多线程情况下数据的正确性。
from threading import * num = 0 def run1(lock): global num for x in range(1000000): lock.acquire( ) # 将程序锁定 num += 1 lock.release( ) # 解锁 print(num) def run2(lock): global num for x in range(1000000): lock.acquire() # 将程序锁定 num += 1 lock.release() # 解锁 print(num) if __name__ == '__main__': lock = Lock() # 创建互斥锁,用于解决非线程安全问题 t1 = Thread(target=run1,args=(lock,)) t1.start() t2 = Thread(target=run2,args=(lock,)) t2.start() # 运行结果:1993755 # 2000000如果不加互斥锁,运行的结果应该会小于2000000,可参考12.系统编程(多进程和多线程)
from threading import Thread,Lock import time myLock1 = Lock() myLock2 = Lock() myLock3 = Lock() # 通过加锁来实现一个同步案例,同步即多线程中的协同步调 def run1(): while True: if myLock1.acquire(): print("run1") myLock2.release() time.sleep(1) def run2(): while True: if myLock2.acquire(): print("run2") myLock3.release() time.sleep(1) def run3(): while True: if myLock3.acquire(): print("run3") myLock1.release() time.sleep(1) if __name__ == '__main__': myLock2.acquire() myLock3.acquire() t1 = Thread(target=run1) t1.start() t3 = Thread(target=run3) t3.start() t2 = Thread(target=run2) t2.start() # 运行结果: # run1 #run2 # run3 # run1 #run2 #run3 ......
在线程间共享多个资源的时候,如果两个线程分别占有⼀部分资源并且同时等待对⽅的资源,就会造成死锁。
尽管死锁很少发⽣,但⼀旦发⽣就会造成应⽤的停⽌响应。
from threading import Thread,Lock import time # 申请两个全局锁 myLock1= Lock() myLock2= Lock() def run1(): print("第一个子线程运行 ") if myLock1.acquire(timeout=2): # 等待2s print("lock1已经加锁了") time.sleep(1) # 人为的代码停止一下 if myLock2.acquire(): # 返回布尔值,表示加锁成功,第一次返回布尔值,第二次卡死 print("第一个执行了吗?") myLock2.release() myLock1.release() def run2(): print("第二个子线程运行了") if myLock2.acquire(): print("lock2已经加锁了") time.sleep(1) if myLock1.acquire(): print("第二个执行了吗?") myLock1.release() myLock2.release() if __name__ == '__main__': t1 = Thread(target=run1) t1.start() t2 = Thread(target=run2) t2.start() # 运行结果: # 第一个子线程运行 # lock1已经加锁了 # 第二个子线程运行了 # lock2已经加锁了 可以用递归加锁的方式解决死锁现象:rlock(),或者银行家算法
七、队列
八、Threadlocal
在多线程环境下,每个线程都有⾃⼰的数据。⼀个线程使⽤⾃⼰的局部变量⽐使⽤全局变量好,
因为局部变量只有线程⾃⼰能看见,不会影响其他线程,⽽全局变量的修改必须加锁。
from threading import Thread, local mylocal = local() # 先new一个local对象 def printMsg(): print(mylocal.name, mylocal.age, mylocal.gender) def speak(): print("%s说了一句话" % mylocal.name) def run1(): # 自身函数中需要的参数绑定到本地线程Threadlocal上 mylocal.name = "张三" mylocal.age = 18 mylocal.gender = "女" printMsg() speak() def run2(): mylocal.name = "李四" mylocal.age = 25 mylocal.gender = "男" printMsg() speak() if __name__ == '__main__': t1 = Thread(target=run1) t2 = Thread(target=run2) t1.start() t2.start() # 运行结果: 张三 18 女 # 张三说了一句话 # 李四 25 男 # 李四说了一句话