爬虫
第五天
多线程
线程的创建和运行
import multiprocessing
def task(num):
pass
p1 = multiprocessing.Process(target = task)
p1.start()
守护线程
一般情况下,主线程代码执行完毕后不会结束,会等待所有子线程任务结束后才结束
如果将子线程设置为守护线程,意味着:主线程代码执行完毕,主线程和子线程一起立即结束
t.setDaemom(True)
多线程的队列
from queue import Queue
q = Queue()
q.put() # 数据放入队列,队列计数+1
q.get() # 从队列中取出数据,队列计数不变
q.task_done() # 和q.gert()配合使用,队列计数-1
q.join() # 阻塞住线程
多进程
进程的创建和运行
import multiprocessing
def task(num):
pass
p1 = multiprocessing
p1.start()
守护进程
将子进程设置为守护进程后,主进程代码执行完毕,主进程和子进程一起结束
p1.deamon=True
多进程的队列
from multiprocessing import JoinableQueue as Queue
线程池
使用线程池多好处
- 降低资源消耗.通过重复利用已经创建的线程降低线程创建和销毁造成多消耗
- 提高响应速度.当任务到达时,任务可以直接执行,而不需要等待新线程的创建
- 提高线程的可管理性.线程时稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性
- 使用线程池可以进行统一的分配,调优和监控
线程池的用法
from multiprocessing.dummy import Pool
def task():
pass
pool = Pool(5) # 线程池中子线程的最大数量,当前为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)是cpython解释器中的,限制了多线程,导致统一时刻只有一个线程被调用,而且不能充分发挥CPU多核的特性
GIL的步骤
- 切换到一个线程
- 设置GIL锁
- 执行指定字节数量的代码
- 代码执行完毕
- 遇到延迟操作 「time.sleep(), socket.accept(), socket.connect()」等
- 释放GIL锁
- 切换到其他线程继续执行
并发和并行
- 有多核心,每个核心执行一个任务,任务数量 <= 核心数,此时并行
- 任务数量 >= 核心数此时并发