一、多进程实现

multiprocess.process模块

process类
  Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
  强调:
  1. 需要使用关键字的方式来指定参数
  2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
 
  参数介绍:
  group参数未使用,值始终为None
  target表示调用对象,即子进程要执行的任务
  args表示调用对象的位置参数元组,args=(1,2,'egon',)
  kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
  name为子进程的名称
 
 process类的方法
  1. p.start():启动进程,并调用该子进程中的p.run()
  2. p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
  3. p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
  4. p.is_alive():如果p仍然运行,返回True
  5. p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能joinstart开启的进程,而不能joinrun开启的进程
 
3.process类的属性
  1. p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
  2. p.name:进程的名称
  3. p.pid:进程的pid
  4. p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
  5. p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
 
 
4.在windows运行时要注意的问题
在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ ==‘__main__’ 判断保护起来,import 的时候  ,就不会递归运行了。
 
 
1创建一个子进程,查看子进程和父进程的进程号
  1. from multiprocessing import Process
  2. import os
  3. def func():
  4. print('子进程PID:',os.getpid())
  5.  
  6. if __name__ == '__main__':
  7. p = Process(target=func)
  8. p.start()
  9. print('父进程PID:',os.getpid())
 
2开启多个子进程
  1. from multiprocessing import Process
  2. import time
  3. def func(no,*args):
  4. print(str(no)+" :"+'*'*args[0])
  5. time.sleep(5)
  6. print(str(no)+" :"+'*'*args[1])
  7. if __name__ == '__main__':
  8. p_li = []
  9. for i in range(10):
  10. p_li.append(Process(target=func,args=(i,10,20)))
  11. for i in p_li:
  12. i.start()
  13.  
  14. [i.join() for i in p_li] #让最后的print等子进程都结束了再执行
  15. print('运行完了')
3 实现多进程的另一种方法
  1. #自定义类 继承Process类
  2. #必须实现run方法,run方法就是子进程执行的方法
  3. #如果要参数,则实现自己的init方法,并在其中调用父类的init方法
  4. from multiprocessing import Process
  5. import os
  6. class MyProcess(Process):
  7. def __init__(self,arg1):
  8. super().__init__()
  9. self.arg1 = arg1
  10. def run(self):
  11. print("My Process:",self.pid)
  12. print(self.arg1)
  13. if __name__ == '__main__':
  14. print(os.getpid())
  15. p1 = MyProcess(4)
  16. p1.start()
 
4.进程间的数据隔离
  1. #进程间不会共享数据
  2. from multiprocessing import Process
  3. import os
  4. def func():
  5. global n
  6. n = 0
  7. print('pid:'+str(os.getpid())+" "+str(n))
  8. if __name__ == '__main__':
  9. n = 100
  10. p = Process(target=func)
  11. p.start()
  12. p.join()
  13. print('pid:'+str(os.getpid())+" "+str(n))
 
 
5.守护进程
守护进程(daemon)是一类在后台运行的特殊进程,用于执行特定的系统任务。很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。
会随着主进程的结束而结束。
主进程创建守护进程
  其一:守护进程会在主进程代码执行结束后就终止
  其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
  1. #守护进程
  2. from multiprocessing import Process
  3. import time
  4. def func():
  5. while 1:
  6. time.sleep(2)
  7. print('Good')
  8. if __name__ == '__main__':
  9. p = Process(target=func)
  10. p.daemon = True #设置子进程为守护进程
  11. p.start()
  12. i = 10
  13. while i>0:
  14. print('Do something')
  15. time.sleep(5)
  16. i -= 1

6、进程间通信

6.1.队列Queue

Queue([maxsize])
创建共享的进程队列。
参数 :maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。
底层队列使用管道和锁定实现。
 
方法
  1. Queue([maxsize])
  2. 创建共享的进程队列。maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。底层队列使用管道和锁定实现。另外,还需要运行支持线程以便队列中的数据传输到底层管道中。
  3. Queue的实例q具有以下方法:
  4.  
  5. q.get( [ block [ ,timeout ] ] )
  6. 返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止。block用于控制阻塞行为,默认为True. 如果设置为False,将引发Queue.Empty异常(定义在Queue模块中)。timeout是可选超时时间,用在阻塞模式中。如果在制定的时间间隔内没有项目变为可用,将引发Queue.Empty异常。
  7.  
  8. q.get_nowait( )
  9. q.get(False)方法。
  10.  
  11. q.put(item [, block [,timeout ] ] )
  12. item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。block控制阻塞行为,默认为True。如果设置为False,将引发Queue.Empty异常(定义在Queue库模块中)。timeout指定在阻塞模式中等待可用空间的时间长短。超时后将引发Queue.Full异常。
  13.  
  14. q.qsize()
  15. 返回队列中目前项目的正确数量。此函数的结果并不可靠,因为在返回结果和在稍后程序中使用结果之间,队列中可能添加或删除了项目。在某些系统上,此方法可能引发NotImplementedError异常。
  16.  
  17. q.empty()
  18. 如果调用此方法时 q为空,返回True。如果其他进程或线程正在往队列中添加项目,结果是不可靠的。也就是说,在返回和使用结果之间,队列中可能已经加入新的项目。
  19.  
  20. q.full()
  21. 如果q已满,返回为True. 由于线程的存在,结果也可能是不可靠的(参考q.empty()方法)。。
简单使用:
  1. from multiprocessing import Process,Queue
  2. if __name__ == '__main__':
  3. q = Queue(5) #创建队列
  4. for i in range(5):
  5. q.put(i) #放进数据
  6. print(q.full())
  7. #q.put(6) 此处阻塞
  8. for i in range(5):
  9. print(q.get()) #获取数据
  10. print(q.empty())
  11. #q.get() 此处阻塞
简单的进程间通信:
  1. from multiprocessing import Event,Process,Queue
  2. def produce(q):
  3. q.put('from produce')
  4. def comsume(q):
  5. print(q.get())
  6. if __name__ == '__main__':
  7. q = Queue(5) #创建队列
  8. pro = Process(target=produce,args=(q,))
  9. pro.start()
  10. com = Process(target=comsume, args=(q,))
  11. com.start()
 
生产者消费者模型
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
 
1)使用Queue
  1. from multiprocessing import Process,Queue
  2. import time
  3. import random
  4. def producer(name,goods,q):
  5. for i in range(10):
  6. time.sleep(random.randint(1,4))
  7. print('%s生产了第%s个%s'%(name,i,goods))
  8. q.put('第%s个%s'%(i,goods))
  9. def comsumer(q,name):
  10. while 1:
  11. goods = q.get()
  12. if goods == None:break
  13. print('\033[31m%s买了了%s\033[0m' % (name,goods))
  14. time.sleep(random.randint(2,6))
  15. if __name__ == '__main__':
  16. q = Queue(10)
  17. p = Process(target=producer,args=('HSR','牛奶',q))
  18. p2 = Process(target=producer, args=('TTT', '面包', q))
  19. c = Process(target=comsumer, args=(q,'Lisi'))
  20. c2 = Process(target=comsumer, args=(q, 'ZhangSan'))
  21. p.start()
  22. p2.start()
  23. c.start()
  24. c2.start()
  25. p.join()
  26. p2.join()
  27. q.put(None)
  28. q.put(None)
 
 
2)使用JoinableQueue
创建可连接的共享进程队列。这就像是一个Queue对象,但队列允许项目的使用者通知生产者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。
  1. from multiprocessing import Process,JoinableQueue
  2. import time
  3. import random
  4. def producer(name,goods,q):
  5. for i in range(10):
  6. time.sleep(random.randint(1,4))
  7. print('%s生产了第%s个%s'%(name,i,goods))
  8. q.put('第%s个%s'%(i,goods))
  9. q.join() #阻塞,直到队列中的数据被全部执行完毕
  10. def comsumer(q,name):
  11. while 1:
  12. goods = q.get()
  13. if goods == None:break
  14. print('\033[31m%s买了了%s\033[0m' % (name,goods))
  15. time.sleep(random.randint(2,6))
  16. q.task_done() #count - 1
  17. if __name__ == '__main__':
  18. q = JoinableQueue(10)
  19. p = Process(target=producer,args=('HSR','牛奶',q))
  20. p2 = Process(target=producer, args=('TTT', '面包', q))
  21. c = Process(target=comsumer, args=(q,'Lisi'))
  22. c2 = Process(target=comsumer, args=(q, 'ZhangSan'))
  23. p.start()
  24. p2.start()
  25. c.daemon = True #设置为守护进程,主进程结束则子进程结束,而这里的主进程等待生产进程的结束
  26. c2.daemon = True #生产进程又等待消费进程消费完。所以消费者消费完了就会结束进程
  27. c.start()
  28. c2.start()
  29. p.join()
  30. p2.join()
 

6.2.管道

multiprocessing.Pipe
 
 
  1. from multiprocessing import Pipe,Process
  2. def func(conn1,conn2):
  3. conn2.close()
  4. while 1:
  5. try:
  6. print(conn1.recv())
  7. except EOFError:
  8. conn1.close()
  9. break
  10. if __name__ == '__main__':
  11. conn1, conn2 = Pipe()
  12. p1 = Process(target=func,args=(conn1, conn2)) #传给不同进程的conn是不会相互影响的
  13. p1.start()
  14. conn1.close()
  15. for i in range(20):
  16. conn2.send("hi")
  17. conn2.close()
 
 
 
使用管道实现生产者消费者模型
  1. #Pipe有数据不安全性
  2. #管道可能出现一端的多个消费者同时取一个数据
  3. #所以可以加上一个进程锁来保证安全性
  4. from multiprocessing import Pipe,Process,Lock
  5. import time
  6. import random
  7. def producer(con,pro,name,goods):
  8. con.close()
  9. for i in range(8):
  10. time.sleep(random.randint(1,3))
  11. print('%s生成了第%s个%s'%(name,i,goods))
  12. pro.send('第%s个%s'%(i,goods))
  13. pro.close()
  14. def consumer(con,pro,name,lock):
  15. pro.close()
  16. while 1:
  17. try:
  18. lock.acquire()
  19. goods = con.recv()
  20. lock.release()
  21. print('%s喝了%s'%(name,goods))
  22. time.sleep(random.random())
  23. except EOFError:
  24. lock.release() #因为最后消费者通过异常来结束进程,所以最后一次的recv后面的lock.release不会执行,所以要在
  25. #这个地方再写一个release()
  26. con.close()
  27. break
  28. if __name__ == '__main__':
  29. con, pro = Pipe()
  30. lock = Lock()
  31. p = Process(target=producer, args=(con,pro,'HSR','牛奶'))
  32. c = Process(target=consumer, args=(con, pro, 'TTT',lock))
  33. c2 = Process(target=consumer, args=(con, pro, 'TTT2',lock))
  34. p.start()
  35. c.start()
  36. c2.start()
  37. con.close()
  38. pro.close()

6.3.Manager

multiprocessing.Manager模块
 
#报 AttributeError: 'ForkAwareLocal' object has no attribute 'connection' 的原因
#运行这段代码时,主进程执行完了,断开了连接,而子进程要连接,此时会报错
#所以可以用join(),让主进程等待子进程的结束
  1. from multiprocessing import Manager,Process
  2. def func(dic):
  3. dic['count'] -= 1
  4. print(dic)
  5. if __name__ == '__main__':
  6. m = Manager() 创建一个Manger()
  7. dic = m.dict({'count':100}) #变成进程共享的字典
  8. p = Process(target=func, args=(dic,))
  9. p.start()
  10. p.join() #等待子进程结束
 
这里会有进程抢占造成的数据不安全问题,通过加锁解决
 
  1. from multiprocessing import Manager,Process,Lock
  2. def work(d,lock):
  3. with lock: #不加锁而操作共享的数据,肯定会出现数据错乱
  4. d['count']-=1
  5.  
  6. if __name__ == '__main__':
  7. lock=Lock()
  8. with Manager() as m:
  9. dic=m.dict({'count':100})
  10. p_l=[]
  11. for i in range(100):
  12. p=Process(target=work,args=(dic,lock))
  13. p_l.append(p)
  14. p.start()
  15. for p in p_l:
  16. p.join()
  17. print(dic)

二、进程池实现

1.为什么要有进程池?
在程序实际处理问题过程中,忙时会有成千上万的任务需要被执行,闲时可能只有零星任务。那么在成千上万个任务需要被执行的时候,我们就需要去创建成千上万个进程么?首先,创建进程需要消耗时间,销毁进程也需要消耗时间。第二即便开启了成千上万的进程,操作系统也不能让他们同时执行,这样反而会影响程序的效率。因此我们不能无限制的根据任务开启或者结束进程。那么我们要怎么做呢?
在这里,要给大家介绍一个进程池的概念,定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,等到处理完毕,进程并不关闭,而是将进程再放回进程池中继续等待任务。如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。也就是说,池中进程的数量是固定的,那么同一时间最多有固定数量的进程在运行。这样不会增加操作系统的调度难度,还节省了开闭进程的时间,也一定程度上能够实现并发效果。
 
2.multiprocess.Pool模块
Pool([numprocess [,initializer [, initargs]]]):创建进程池
 
参数
  1. numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
  2. initializer:是每个工作进程启动时要执行的可调用对象,默认为None
  3. initargs:是要传给initializer的参数组
 
2.方法
  1. p.apply(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
  2. '''需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async()'''
  3.  
  4. p.apply_async(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
  5. '''此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。'''
  6.  
  7. p.close():关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
  8.  
  9. P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用
  10.  
  11. 方法apply_async()和map_async()的返回值是AsyncResul的实例obj。实例具有以下方法
  12. obj.get():返回结果,如果有必要则等待结果到达。timeout是可选的。如果在指定时间内还没有到达,将引发一场。如果远程操作中引发了异常,它将在调用此方法时再次被引发。
  13. obj.ready():如果调用完成,返回True
  14. obj.successful():如果调用完成且没有引发异常,返回True,如果在结果就绪之前调用此方法,引发异常
  15. obj.wait([timeout]):等待结果变为可用。
  16. obj.terminate():立即终止所有工作进程,同时不执行任何清理或结束任何挂起工作。如果p被垃圾回收,将自动调用此函数
 
 
 
3.实例
1)简单例子1(使用map)
  1. #map如果要给函数传参数,只能传可迭代对象
  2. from multiprocessing import Pool
  3. def func(dic):
  4. print(dic)
  5. def func2(dic):
  6. print(dic+2)
  7. if __name__ == '__main__':
  8. pool = Pool(5) #进程数,CPU核心数+1
  9. #如果Pool()不传参数,默认是cpu核心数
  10. pool.map(func2,range(100)) #100个任务
  11. #这里自带join效果
  12. pool.map(func, ['hsr','ttt']) # 2个任务
 
 
2)简单例子2(使用apply)
  1. from multiprocessing import Pool
  2. import os
  3. import time
  4. def func(n):
  5. print('[pid:%s]start id:%s'%(os.getpid(),n))
  6. time.sleep(1.5)
  7. print('\033[31m[pid:%s]end id:%s\033[0m'%(os.getpid(),n))
  8.  
  9. if __name__ == '__main__':
  10. pool = Pool(5)
  11. for i in range(10):
  12. #pool.apply(func,args=(i,)) #同步
  13. pool.apply_async(func,args=(i,)) #异步。与主进程完全异步,需要手动close和join
  14.  
  15. pool.close() # 结束进程池接收任务
  16. pool.join() # 感知进程中的任务都执行结束
 
4.回调函数
需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数
我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。
 
  1. from multiprocessing import Pool
  2. def func(i):
  3. print('in func1')
  4. return i**2
  5. def func2(n):
  6. print('in func2')
  7. print(n)
  8. if __name__ == '__main__':
  9. pool = Pool(5)
  10. pool.apply_async(func, args=(10,), callback=func2) #执行func1,把返回值作为fun2的参数执行func2
  11. #回调函数func2在主进程中zhi'x
  12. pool.close()
  13. pool.join()

简单例子:
  1. import requests
  2. from multiprocessing import Pool
  3.  
  4. def get(url):
  5. ret = requests.get(url)
  6. if ret.status_code == 200:
  7. return ret.content.decode('utf-8'),url
  8.  
  9. def call_back(args):
  10. print(args[1] +" "+ str(len(args[0])))
  11.  
  12. url_lst = [
  13. 'http://www.cnblog.com',
  14. 'https://www.baidu.com',
  15. 'http://www.sohu.com'
  16. ]
  17.  
  18. if __name__ == '__main__':
  19. pool = Pool(5)
  20. for i in url_lst:
  21. pool.apply_async(get,args=(i,),callback=call_back)
  22. pool.close()
  23. pool.join()
 
三、多进程爬虫实例
 
实例 抓取b站up主视频评论
 1.普通多进程
  1. import re
  2. import requests
  3. import json
  4. import threading
  5. import math
  6. import time
  7. from multiprocessing import Process, Queue
  8.  
  9. HEADERS = {#'Accept':"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
  10. 'User-Agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
  11.  
  12. }
  13.  
  14. # Thread-local state to stored information on locks already acquired
  15.  
  16. def start_urls(tasks,total_page):
  17. #生产者 产生用于消费的urls任务列表
  18.  
  19. url = "https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn={}&type=1&oid=455312953&sort=2&_=1587372277524"
  20. for i in range(1,total_page+1):
  21. tasks.put(url.format(i))
  22. return tasks
  23.  
  24. def init_start():
  25. #获取评论列表的总页数
  26. url = "https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn=1&type=1&oid=455312953&sort=2&_=1587372277524"
  27. content = downloader(url)
  28. data = json.loads(content.text)
  29. total_page = math.ceil(int(data['data']['page']['count'])/int(data['data']['page']['size']))
  30. print(total_page)
  31. return total_page
  32.  
  33. def downloader(url):
  34. #下载任务
  35. content = requests.get(url,headers=HEADERS)
  36. print(content.status_code,type(content.status_code))
  37. return content
  38.  
  39. def work(tasks,n):
  40. #消费者
  41. while not tasks.empty():
  42. time.sleep(1)
  43. try:
  44. url = tasks.get()
  45. except Exception as e:
  46. print('e',e)
  47. continue
  48. print(url)
  49. data = downloader(url)
  50.  
  51. if __name__ == '__main__':
  52. tasks = Queue()
  53. total_page = init_start()
  54. task_urls = start_urls(tasks,total_page)
  55. print(tasks.qsize())
  56. for i in range(3):
  57. t = Process(target=work,args=(tasks,i))
  58. t.start()

2.进程池

  1. import re
  2. import requests
  3. import json
  4. import threading
  5. import math
  6. import time
  7. from multiprocessing import Process, Queue
  8. from multiprocessing import Manager,Pool
  9.  
  10. HEADERS = {#'Accept':"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
  11. 'User-Agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
  12.  
  13. }
  14.  
  15. # Thread-local state to stored information on locks already acquired
  16.  
  17. def start_urls(tasks,total_page):
  18. #生产者 产生用于消费的urls任务列表
  19.  
  20. url = "https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn={}&type=1&oid=455312953&sort=2&_=1587372277524"
  21. for i in range(1,total_page+1):
  22. tasks.put(url.format(i))
  23. return tasks
  24.  
  25. def init_start():
  26. #获取评论列表的总页数
  27. url = "https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn=1&type=1&oid=455312953&sort=2&_=1587372277524"
  28. content = downloader(url)
  29. data = json.loads(content.text)
  30. total_page = math.ceil(int(data['data']['page']['count'])/int(data['data']['page']['size']))
  31. print(total_page)
  32. return total_page
  33.  
  34. def downloader(url):
  35. #下载任务
  36. content = requests.get(url,headers=HEADERS)
  37. print(content.status_code,type(content.status_code))
  38. return content
  39.  
  40. def work(tasks,n):
  41. #消费者
  42. while not tasks.empty():
  43. time.sleep(1)
  44. try:
  45. url = tasks.get()
  46. except Exception as e:
  47. print('e',e)
  48. continue
  49. print(url)
  50. data = downloader(url)
  51.  
  52. if __name__ == '__main__':
  53. manager = Manager()
  54. tasks = manager.Queue()#注意进程间的通信
  55. total_page = init_start()
  56. task_urls = start_urls(tasks,total_page)
  57. p = Pool(3)
  58. for i in range(3):
  59. p.apply_async(work,args=(task_urls,i))
  60. p.close()
  61. p.join()

利用concurrent.futures

  1. import re
  2. import requests
  3. import json
  4. import threading
  5. import math
  6. import time
  7. from multiprocessing import Manager,Pool
  8. from concurrent.futures import ProcessPoolExecutor,as_completed
  9.  
  10. HEADERS = {#'Accept':"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
  11. 'User-Agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
  12.  
  13. }
  14.  
  15. # Thread-local state to stored information on locks already acquired
  16.  
  17. def start_urls(tasks,total_page):
  18. #生产者 产生用于消费的urls任务列表
  19.  
  20. url = "https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn={}&type=1&oid=455312953&sort=2&_=1587372277524"
  21. for i in range(1,total_page+1):
  22. tasks.put(url.format(i))
  23. return tasks
  24.  
  25. def init_start():
  26. #获取评论列表的总页数
  27. url = "https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn=1&type=1&oid=455312953&sort=2&_=1587372277524"
  28. content = downloader(url)
  29. data = json.loads(content.text)
  30. total_page = math.ceil(int(data['data']['page']['count'])/int(data['data']['page']['size']))
  31. print(total_page)
  32. return total_page
  33.  
  34. def downloader(url):
  35. #下载任务
  36. content = requests.get(url,headers=HEADERS)
  37. print(content.status_code,type(content.status_code))
  38. return content
  39.  
  40. def work(tasks,n):
  41. #消费者
  42. while not tasks.empty():
  43. time.sleep(1)
  44. try:
  45. url = tasks.get()
  46. except Exception as e:
  47. print('e',e)
  48. continue
  49. print(url)
  50. data = downloader(url)
  51.  
  52. if __name__ == '__main__':
  53. manager = Manager()
  54. tasks = manager.Queue()
  55. total_page = init_start()
  56. task_urls = start_urls(tasks,total_page)
  57.  
  58. with ProcessPoolExecutor(max_workers=5) as executor:
  59. all_task = [executor.submit(work, task_urls,i) for i in range(4)]
  60. for future in as_completed(all_task):
  61. data = future.result()
  62. #print("in main: get page {}s success".format(data))

python 并发专题(三):进程以及进程池相关以及实现的更多相关文章

  1. Python并发编程03 /僵孤进程,孤儿进程、进程互斥锁,进程队列、进程之间的通信

    Python并发编程03 /僵孤进程,孤儿进程.进程互斥锁,进程队列.进程之间的通信 目录 Python并发编程03 /僵孤进程,孤儿进程.进程互斥锁,进程队列.进程之间的通信 1. 僵尸进程/孤儿进 ...

  2. python并发编程02 /多进程、进程的创建、进程PID、join方法、进程对象属性、守护进程

    python并发编程02 /多进程.进程的创建.进程PID.join方法.进程对象属性.守护进程 目录 python并发编程02 /多进程.进程的创建.进程PID.join方法.进程对象属性.守护进程 ...

  3. python并发编程基础之守护进程、队列、锁

    并发编程2 1.守护进程 什么是守护进程? 表示进程A守护进程B,当被守护进程B结束后,进程A也就结束. from multiprocessing import Process import time ...

  4. python 并发专题(一):并发基础相关概念,术语等

    一.线程 1.概念 线程是程序执行流的最小执行单位,是行程中的实际运作单位. 进程是一个动态的过程,是一个活动的实体.简单来说,一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执 ...

  5. python 并发专题(六):协程相关函数以及实现(gevent)

    文档资源 http://sdiehl.github.io/gevent-tutorial/ 一.协程实现 线程和协程 既然我们上面也说了,协程也被称为微线程,下面对比一下协程和线程: 线程之间需要上下 ...

  6. python 并发专题(十):基础部分补充(二)线程

    什么是线程 标准描述开启一个进程:开启一个进程:进程会在内存中开辟一个进程空间,将主进程的资料数据全部复制一份,线程会执行里面的代码. ***进程是资源单位, 线程是执行单位:是操作系统调度的最小单元 ...

  7. Python并发复习4- concurrent.futures模块(线程池和进程池)

    Python标准库为我们提供了threading(多线程模块)和multiprocessing(多进程模块).从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提 ...

  8. Python 并发编程(管道,事件,信号量,进程池)

    管道 Conn1,conn2 = Pipe() Conn1.recv() Conn1.send() 数据接收一次就没有了 from multiprocessing import Process,Pip ...

  9. python 并发专题(九):基础部分补充(一)进程

    概念 串行:所有的任务一个一个的完成. 并发:一个cpu完成多个任务.看起来像是同时完成. 并行:多个cpu执行多个任务,真正的同时完成. 阻塞:cpu遇到IO就是阻塞. 非阻塞:没有IO,就叫非阻塞 ...

随机推荐

  1. java中的引用类型:强软弱虚

    java中的引用类型共4种:强软弱虚,具体每种类型的特点和应用场景.记录下.本文是看了马士兵老师的视频后记录整理的.加深印象. 基本概念 1. 强引用 强引用是使用最普遍的引用.如果一个对象具有强引用 ...

  2. ubuntu安装mysql并使用Navicat连接

    今天配置了一下自己的服务器,发现网上很多教程都有点老,而且不是很全.干脆就写一篇Ubuntu安装mysql,并用Navicat连接的全流程 一.安装mysql 1. sudo apt-get inst ...

  3. D2大全

    年初看到cnblogs上有人说看这本旧书,自己也只是瞟了下,后来在看些OOP东西时,想想没事也看看老古董,于是网购了一本电子版可参考下,它们是怎么一步步来,还没来得及多看,贴图于此.

  4. 在MS SQL(SSMS中)_Format_SQL_更改设置_增加命令

    在MS SQL(SSMS中)_Format_SQL_更改设置_增加命令 目的:要格式化这么一段SQL语句(这是随便从网上Copy的一段),没细看内容,反正看到头疼,乱七八糟的不想看. select b ...

  5. linux使用组ID(SGID)共享文件

    假如你有这样一个需求,一个小组内很多成员共同研究一个项目,为了这个项目我们需要分配一个具体的目录. 所有成员都拥有该目录的使用权限,可以互相操作成员的文件及内容.而且不允许其他人查看. 现在开始操作: ...

  6. CPU性能分析工具原理

    转载请保留以下声明 作者:赵宗晟 出处:https://www.cnblogs.com/zhao-zongsheng/p/13067733.html 很多软件都要做性能分析和性能优化.很多语言都会有他 ...

  7. JSON案例

    原文链接:https://zhuanlan.zhihu.com/p/62763428 json字符串->JSONObject 用JSON.parseObject()方法即可将JSon字符串转化为 ...

  8. IE11下文档模式默认值是7, 而且无法更改

    IE9以上是支持css3的,但是有的IE11的浏览器里面,文档模式默认值是7,而且是无法改变的,就会导致网页布局错乱 我的IE11的文档模式默认值是11 ,如下图  (打开页面按F12) 对于默认值是 ...

  9. python的坑--你知道吗?

    python的坑--你知道吗? 1.列表的坑 坑的地方是:因为列表用pop之后,后面的索引都会自动减一 # 列表的坑之一 list1 = ['python','java','php','c','c++ ...

  10. 利用synchronized解析死锁的一种形成方式

    代码 import ... public class Test{ private static Object o1=new Object(); private static Object o2=new ...