网站服务器的采购方案,tag改为静态wordpress,wordpress七牛云token,网站二维码链接怎么做以往的爬虫我们都采用单线程和同步的方式#xff0c;这导致我们的爬虫及其脆弱#xff0c;因为一点报错都会让它停下来#xff0c;而且面对比较大的数据#xff0c;爬虫只能选择等待#xff0c;这种阻塞会消耗很多时间#xff0c;为什么我们不把等待的这些时间去干别的事… 以往的爬虫我们都采用单线程和同步的方式这导致我们的爬虫及其脆弱因为一点报错都会让它停下来而且面对比较大的数据爬虫只能选择等待这种阻塞会消耗很多时间为什么我们不把等待的这些时间去干别的事呢 线程与进程 线程和进程是相似的 一概念梳理 线程 程序内可以直接被CPU调用的执行过程是操作系统能够进行运算的最小单位它被包含在进程中实际运作的单位。 进程运行中的程序每次我们执行应该程序操作系统会自动地为这个程序准备一些必要的资源分配内存创造一个能执行的线程 形象来说线程就像是员工进程就是公司线程组成进程。如果我们想要提升效率我们可以多招些员工多线程或者开一些分公司连锁多进程 二代码实现
多线程
from threading import Thread
# def func(name):
# for i in range(10):
# print(name,i)#创建任务
def func_1(name):for i in range(100):print(name,i)if __name__ __main__:# func(A)# func(B)# func(C)# #创建线程t1 Thread(targetfunc_1,args(A,))t2 Thread(targetfunc_1,args(B,))t3 Thread(targetfunc_1,args(C,))t1.start()t2.start()t3.start() 实例化线程对象Thread(taeget目标函数不要括号args()填参数元组形式) 开启线程线程对象的start方法 面向对象写法
from threading import Thread
class MyThread(Thread):def __init__(self,name):super(MyThread, self).__init__()self.name namedef run(self):for i in range(100):print(self.name,i)if __name__ __main__:t1 MyThread(A)t2 MyThread(B)t3 MyThread(C)t1.start()t2.start()t3.start()需要注意的是函数执行的任务必须重写到run方法中其他的一样。
很快一个一个实例化线程对象的方法无法满足我们了如果我们要申请20个线程呢这太繁琐了 线程池的产生就是顺其自然的了 线程池
from concurrent.futures import ThreadPoolExecutordef func(name):for i in range(50):print(name,i)if __name__ __main__:with ThreadPoolExecutor(10) as t:t.submit(func,A)t.submit(func,B)t.submit(func,C)
这大大地减轻了我们的工作量我们只需要把任务丢进线程池里它就会自动分配线程为我们处理当然最大线程数我们可以设置上例使用了10个。
好了上面的所有情况我们都忽视掉了函数有返回值的情况是那么这种情况该怎么处理呢
from concurrent.futures import ThreadPoolExecutordef func(name):print(name)return namedef fun(res):print(res.result())if __name__ __main__:with ThreadPoolExecutor(3) as t:t.submit(func,A).add_done_callback(fun)t.submit(func,B).add_done_callback(fun)t.submit(func,C).add_done_callback(fun)或者
from concurrent.futures import ThreadPoolExecutordef func(name):print(name)return namedef fun(res):print(res.result())if __name__ __main__:with ThreadPoolExecutor(3) as t:results t.map(func,[A,B,C])for result in results:print(result) 这样我们就可以获取函数返回值了。 多进程
from multiprocessing import Process
from concurrent.futures import ProcessPoolExecutor
def func(name):for i in range(100):print(name,i)if __name__ __main__:p1 Process(targetfunc,args(A,))p2 Process(targetfunc,args(B,))p3 Process(targetfunc,args(C,))p1.start()p2.start()p3.start() 怎么样对比一下多线程你会发现代码上除了调的类不一样以外就没区别了多进程的进程池和线程池几乎一模一样这里就不赘述了可以自己尝试一下进程池上面代码有名字~ 线程和进程是相似的 那么多进程和多线程的应用场景有什么不同呢 多线程任务相对统一互相特别相似
多进程多个任务相互独立很少有交集 多任务异步协程 协程协程是一种比函数更加强大的控制流结构它可以挂起暂停自身的执行并在稍后从上次挂起的地方恢复执行。协程允许在单个线程内进行非阻塞的协作式多任务处理这意味着程序可以在等待某个耗时操作如I/O操作、网络请求完成的同时去做其他事情从而提高整体效率。协程拥有自己的执行上下文包括局部变量和指令指针可以在多个协程之间方便地切换。 简单理解多线程可以理解为我们不停的切换cpu运算对象任务不动CPU动。而协程是单线程中通过移动任务来实现的比如任务1进入这条线程中进行计算.....直到产生IO阻塞任务1就出线程任务2进入线程以此类推当任务1IO阻塞结束再让它回到线程如果还有必要的话。这个过程实质上是CPU不动任务移动。这其实效率更高因为CPU调用切换是需要消耗内存的它是属于系统层面的问题而协程只需要切换任务这是代码层面的问题实际上是不需要多少时间和空间的。 异步协程代码实现
import asyncioasync def func():print(我是函数)if __name__ __main__:#协程对象想要执行必须借助于 event_loopf func()event_loop asyncio.get_event_loop()event_loop.run_until_complete(f)#eventloop运行协程对象直到该对象内的内容执行完毕为止 这是协程的基本结构async表示异步因为我们需要IO阻塞想象一下如果我们申请网页响应中不执行IO阻塞那在申请的同时代码就已经跑到下面去了如果我们下面需要这个网页的源码呢这就必然报错了所以这就是为什么我们的函数使用了异步操作本质还是在IO阻塞上。 import asyncioasync def func1():print(我是func1)await asyncio.sleep(1)print(func1结束)async def func2():print(我是func2)await asyncio.sleep(2)print(func2结束)async def func3():print(我是func3)await asyncio.sleep(3)print(func3结束)if __name__ __main__:f1 func1()f2 func2()f3 func3()tasks [f1,f2,f3]asyncio.run(asyncio.wait(tasks)) 上例我们创建了三个任务并将这三个任务添加到了协程中wait()为何意这是为了让我们三个任务必须执行完才关闭eventloop,要不然还有任务在io阻塞协程直接关了怎么办。 异步协程的函数返回值
import asyncio
async def func1():print(我是func1)await asyncio.sleep(1)print(func1结束)return func1的返回值async def func2():print(我是func2)await asyncio.sleep(2)print(func2结束)return func2的返回值async def func3():print(我是func3)await asyncio.sleep(3)print(func3结束)return func3的返回值async def main():f1 func1()f2 func2()f3 func3()tasks [asyncio.create_task(f1),asyncio.create_task(f2),asyncio.create_task(f3)]# done,padding await asyncio.wait(tasks)# for res in done:# print(res.result())# gather和wait区别gather返回值是有顺序的按照你添加任务的顺序result await asyncio.gather(*tasks,return_exceptionsFalse)#return exceptiontrue表示如果任务中有错误信息则返回错误信息其他任务正常执行。print(result)
if __name__ __main__:asyncio.run(main()) 解释请看代码注释~ 创作不易点个小赞鼓励一下呗~