爬虫--多线程-多进程-协程以及池的使用

多线程

  1. 线程的创建和运行

    import threading
    
    def task(num):
        pass
    
    t = threading.Thread(target=task,args=(num,))
    t.start()
    
  2. 守护线程

    一般情况下,主线程代码执行完毕后不会结束,会等待所有子线程任务结束后才结束

    如果将子线程设置为守护线程,意味着:主线程代码执行完毕,主线程和子线程一起立即结束

    t.setDaemon(True)
    
  3. 多线程的队列

    from queue import Queue
    q = Queue()
    q.put() # 数据放入队列,队列计数+1
    q.get() # 从队列中取出数据,队列计数不变
    q.task_done() # 和q.get()配合使用,队列计数-1
    
    q.join() #阻塞主线程,等待队列计数为0时,结束阻塞
    

多进程

  1. 进程的创建和运行

    import multiprocessing
    
    def task(num):
        pass
    
    p1 = multiprocessing.Process(target=task)
    p1.start()
    
  2. 守护进程

    将子进程设置为守护进程后,主进程代码执行完毕,主进程和子进程一块结束

    p1.deamon=True
    
  3. 多进程的队列

    from multiprocessing import JoinableQueue as Queue
    # 不能使用普通队列,因为进程之间是资源独立的,需要使用能够让多进程间共享数据的队列
    

线程池

使用线程池的好处:

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,
    使用线程池可以进行统一的分配,调优和监控

线程池的用法

from multiprocessing.dummy import Pool
def task():
    pass

pool = Pool(5) # 线程池中子线程的最大数量

pool.apply_async(func=task,args=(,)) # 发布任务,交给线程池的子线程去执行,只是发布
pool.close() # 关闭线程池,不再接收新的任务,不是销毁线程池
pool.join() # 阻塞主线程,等待线程池中所有子线程的任务结束

线程池的回调:

当不清楚,线程池需要做的任务数量时,可以使用线程池的回调

def _callback(temp):
    print("之前的线程任务执行完毕")
    # 可以在此为线程池添加新的任务去执行

pool.apply_async(func=task,callback=_callback) # 发布任务,交给线程池的子线程去执行,只是发布,当子线程执行完毕时,会自动调用callback

协程池

import gevent.monkey

gevent.monkey.patch_all()
# 协程池
from gevent.pool import Pool
from queue import Queue

# 协程池pool没有close方法

一般情况下,协程池的效率高于线程池

GIL

全局解释器锁(GIL),cpyton解释器中的,限制了多线程,导致同一时刻只有一个线程被调用,而且不能cpu多核的特性

GIL的步骤:

  1. 切换到一个线程

  2. 设置GIL锁

  3. 执行指定字节数量的代码

    • 代码执行完毕
    • 遇到延迟操作比如(time.sleep(), socket.accept(), socket.connect())
  4. 释放GIL锁

  5. 切换到别的线程去执行

由于GIL的限制,对于IO密集型的操作,可以优先使用多线程、协程技术,对于CPU(计算)密集型的操作,使用多进程

并发和并行

有多核心,每个核心执行一个任务,任务数量 <= 核心数,此时时并行

任务数量 >=核心数 ,此时是并发

猜你喜欢

转载自blog.csdn.net/weixin_44090435/article/details/86480708