多线程与多进程的优缺点 与 线程的一些方法
多进程:
优点:可以用多核
缺点:开销大
多线程:
优点:开销小
缺点:不能使用多核
在日常的生活中,我们用到的肯定是多核机器,所以我们只考虑多核的情况,你会说那么根据上面的优缺点,那肯定就用多进程就好了。欧克,那只是你自己的意淫而已,接下来我要解释一波了,请听好:
我们首先确定的点,就是在一个多核的机器上,进行一系列的操作,那么使用多线程好呢?还是多进程好呢?
在这个时候我要提出一个观点:就是CPU肯定是用来做计算的,这是毋庸置疑的。
ok,我们在之前的基础上,考虑两种情况:
1,计算密集的操作:我用代码来征服你们,看哪一个更好
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import Process import time def work(): res = 0 for i in range(11111100): res+=i if __name__ == '__main__': start = time.time() l = [] for i in range(4): p = Process(target=work) l.append(p) p.start() for j in l: j.join() end = time.time() print('%s'%(end - start)) 运行时间:1.794102430343628 #根据机子的不同可能结果也并不同
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread import time def work(): res = 0 for i in range(11111100): res+=i if __name__ == '__main__': start = time.time() l = [] for i in range(4): T = Thread(target=work) l.append(T) T.start() for j in l: j.join() end = time.time() print('%s'%(end - start)) 结果:3.125178813934326
看结果一目了然,进程很快,你很牛逼,我来说说原理:对于多线程来说,上面已经提到,他的缺点就是无法使用多核,由于gil锁的存在,他只能一个一个的取抢锁,所以会慢,多进程则相反
2,i/o密集的操作:依旧用代码来征服你:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread import time def work(): time.sleep(2) if __name__ == '__main__': start = time.time() l = [] for i in range(400): p = Thread(target=work) # p = Process(target=work) l.append(p) p.start() for j in l: j.join() end = time.time() print('%s' % (end - start)) 结果:2.048117160797119
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import Process import time def work(): time.sleep(2) if __name__ == '__main__': start = time.time() l = [] for i in range(400): # p = Thread(target=work) p = Process(target=work) l.append(p) p.start() for j in l: j.join() end = time.time() print('%s' % (end - start)) 结果:from multiprocessing import Process from threading import Thread import time def work(): time.sleep(2) if __name__ == '__main__': start = time.time() l = [] for i in range(400): # p = Thread(target=work) p = Process(target=work) l.append(p) p.start() for j in l: j.join() end = time.time() print('%s' % (end - start)) 结果:19.68112564086914
看结果很明显:我用时间的停留模拟i/o阻塞,进程确实是并发的,但是在i/o阻塞的时候都要等着,无法运行,并且在进程创建的时候开销大,时间长,即使是并发的,我开了400个进程,机已经多出了那么多的时间,可想而知,开更多会是什么样。
应用:
计算密集 多进程 金融领域
i/o密集 多线程 爬虫 web socket
死锁 与递归锁、
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,情况如下:
from threading import Thread,Lock import time mutexA = Lock() mutexB = Lock() class Work(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() print('%s 释放了 B锁' % self.name) mutexA.release() print('%s 释放了 A锁' % self.name) def f2(self): mutexB.acquire() time.sleep(2) print('%s 拿到了B锁 ' % self.name) mutexA.acquire() print('%s拿到了A锁' % self.name) mutexA.release() print('%s 释放了 A锁' % self.name) mutexB.release() print('%s 释放了 B锁' % self.name) if __name__ == '__main__': for i in range(5): t = Work() t.start()
解决方法:
递归锁:Rlock
Rlock内部有一个count 初始为0 ,锁一下加1,释放了就减一。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread,RLock import time mutexA = mutexB = RLock() class Work(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() print('%s 释放了 b锁' % self.name) mutexA.release() print('%s 释放了 a锁' % self.name) def f2(self): mutexB.acquire() time.sleep(2) print('%s 拿到了A锁 ' % self.name) mutexA.acquire() print('%s拿到了B锁' % self.name) mutexA.release() print('%s 释放了 b锁' % self.name) mutexB.release() print('%s 释放了 a锁' % self.name) if __name__ == '__main__': for i in range(5): t = Work() t.start()
信号量Semaphore 他是一种锁
Semaphore ()括号内的参数是几,就说明可以有几个可以用这段锁中的代码,并不是同事哦 谁先运行完,下一个就会进来。
信号量与进程池是有着同一种的用途,但是用法不同,很且差很多
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread, Semaphore, currentThread import time,random sm = Semaphore(5) def work(): sm.acquire() print('\033[32m%s 正在上厕所\033[0m ' % currentThread().name) time.sleep(random.randint(1, 3)) print('\033[30%s 走出了厕所\033[0m' % currentThread().name) sm.release() if __name__ == '__main__': for i in range(20): t = Thread(target=work,) t.start()
Event
线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行
Event的一些方法:
event = Event()
event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; event.clear():恢复event的状态值为False。
具体应用在mysql中
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread, Event, currentThread import time def check_mysql(): print('%s 正在检测mysql... ' % currentThread().name) time.sleep(4) e.set() def conn_mysql(): count = 1 while not e.is_set(): if count > 4: raise ConnectionResetError('链接次数过多') print('%s 正在等待第%s链接mysql' % (currentThread().name, count)) e.wait(timeout=1) count += 1 print('%s 连接成功' % currentThread().name) if __name__ == '__main__': traffic = Thread(target=check_mysql) traffic.start() for i in range(3): t = Thread(target=conn_mysql) t.start()
定时器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Timer def hello(): print("hello, world") t = Timer(1, hello) t.start() # after 1 seconds, "hello, world" will be printed
线程queue
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import queue # q=queue.Queue(3) #先进先出 # q.put('first') # q.put('second') # q.put('third') # # q.put('fourth') # # print(q.get()) # print(q.get()) # print(q.get())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# q=queue.LifoQueue() #先进后出 # q.put('first') # q.put('second') # q.put('third') # # q.put('fourth') # # print(q.get()) # print(q.get()) # print(q.get())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import queue q=queue.PriorityQueue() #put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高 q.put((20,'a')) q.put((10,'b')) q.put((30,'c')) print(q.get()) print(q.get()) print(q.get())
本文来自博客园,作者:Python界-黄药师,转载请注明原文链接:https://www.cnblogs.com/niehaidong111/p/7453718.html