进程:运行中的程序. 每次我们执行一个程序, 咱们的操作系统对自动的为这个程序准备一些必要的资源(例如,分配内存,创建一个能够执行的线程。)
线程:程序内,可以直接被CPU调度的执行过程. 是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
进程与线程之间的关系:
进程是资源单位(公司),线程是执行单位(员工),就好比是一家公司,一家公司的资源就是桌椅板凳,电脑饮水机这些资源,但是,我们如果说一家公司正在运转着,运行着,那里面必须要有能为这家公司工作的人。程序里面也一样,进程就是为了程序运行而需要的各种资源。但是程序想要运行,就必须由线程来被CPU调度执行。
我们运行的每一个程序默认都会有一个线程,哪怕是只有helloworld级别的程序,想要执行,也会有一个线程产生。
线程 顾名思义,多线程就是让程序产生多个线程一起去执行。 还拿公司举例子,一家公司里如果只有一个员工,工作效率肯定不会高到哪里去,怎么提高效率?多招点儿人就OK了。
直接使用Thread创建线程 单线程
1 2 3 4 5 6 7 def work (): for i in range (1000 ): print ("func" , i) if __name__ == '__main__' : work()
多线程
1 2 3 4 5 6 7 8 9 10 11 12 13 import threadingdef work (): for i in range (1000 ): print (f"{threading.current_thread().name} Print: {i} " ) if __name__ == '__main__' : for i in range (10 ): t = threading.Thread(target=work, name=f"threadP{i} " ) t.start()
线程池 python还提供了线程池功能,可以一次性的创建多个线程,并且,不需要我们程序员手动去维护。一切都交给线程池来自动管理。
1 2 3 4 5 6 7 8 9 10 from concurrent.futures.thread import ThreadPoolExecutordef work (name ): for i in range (10000 ): print (f'{name} :{i} ' ) with ThreadPoolExecutor(16 ) as t: for i in range (4 ): t.submit(work, f'线程{i} ' )
进程 一个公司能创造的价值毕竟是有限的,怎么办?开分公司啊.,此所谓多进程,python实现多进程的方案和多线程几乎一样,非常的简单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from multiprocessing import Processdef func (): for i in range (1000 ): print ("func" , i) if __name__ == '__main__' : p = Process(target=func) p.start() for i in range (1000 ): print ("main" , i)
协程 协程是我要重点去讲解的一个知识点,它能够更加高效的利用CPU。
其实, 我们能够高效的利用多线程来完成爬虫其实已经很6了,但是,从某种角度讲,线程的执行效率真的就无敌了么?我们真的充分的利用CPU资源了么?非也~ 比如,我们来看下面这个例子。
我们单独的用一个线程来完成某一个操作,看看它的效率是否真的能把CPU完全利用起来。
1 2 3 4 5 6 7 8 import timedef func (): print ("我爱黎明" ) time.sleep(3 ) print ("我真的爱黎明" ) func()
各位请看,在该程序中,我们的func()实际在执行的时候至少需要3秒的时间来完成操作,中间的三秒钟需要让我当前的线程处于阻塞状态。阻塞状态的线程 CPU是不会来执行你的,那么此时cpu很可能会切换到其他程序上去执行。此时,对于你来说,CPU其实并没有为你工作(在这三秒内),那么我们能不能通过某种手段,让CPU一直为我而工作,尽量的不要去管其他人。
我们要知道CPU一般抛开执行周期不谈,如果一个线程遇到了IO操作,CPU就会自动的切换到其他线程进行执行。那么,如果我想办法让我的线程遇到了IO操作就挂起,留下的都是运算操作,那CPU是不是就会长时间的来照顾我。
以此为目的,伟大的程序员就发明了一个新的执行过程。当线程中遇到了IO操作的时候,将线程中的任务进行切换,切换到其他任务。等原来的IO执行完了,再恢复回原来的任务中。
就形成了这样一种模型,在程序遇到了IO操作(费时不费力的操作)时,自动切换到其他任务,该模型被称为协程。
协程基本使用 需要使用关键词async
和await
。
async
是定义一个异步函数的关键字,异步函数是协程的一种。它用于定义一个可以在执行过程中挂起的函数,该函数可以在遇到await
关键字时挂起,并等待另一个协程执行完毕后再继续执行。
await
则是用于挂起当前协程并等待另一个协程执行完毕的关键字。当遇到await
关键字时,当前协程将会暂停执行,直到等待的协程执行完毕,并返回结果后才会继续执行。
async
/await
的使用可以大大简化异步编程的复杂性,使得在Python
中实现高效的异步程序变得更加容易。通过使用async
/await
,我们可以将一些常见的异步操作抽象为协程,从而使得我们可以使用类似于同步代码的方式来实现异步操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import timeimport asyncioasync def func1 (): print ("func1, start" ) await asyncio.sleep(3 ) print ("func1, end" ) async def func2 (): print ("func2, start" ) await asyncio.sleep(4 ) print ("func2, end" ) async def func3 (): print ("func3, start" ) await asyncio.sleep(2 ) print ("func3, end" ) async def run (): start = time.time() tasks = [ asyncio.create_task(func1()), asyncio.create_task(func2()), asyncio.create_task(func3()), ] await asyncio.wait(tasks) print (time.time() - start) if __name__ == '__main__' : asyncio.run(run())
协程异步IO操作 协程可以结合相关异步的第三方库,达到异步网页请求和文件操作操作,最大化使用CPU资源。
aiohttp:异步HTTP请求库,类似于requests库。
aiofiles:异步文件操作库,一般用于文件读写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import aiohttpimport asyncioimport aiofilesasync def download (url ): try : name = url.split("/" )[-1 ].split('@' )[0 ] async with aiohttp.ClientSession() as session: async with session.get(url, ssl=False ) as resp: content = await resp.content.read() async with aiofiles.open ('../download_files/' + name, mode="wb" ) as f: await f.write(content) return "OK" except Exception as e: print (e) return "NO" async def main (): url_list = [ "https://cdn-usa.skypixel.com/uploads/usa_files/storynode/image/214c3e7a-767d-41b3-9dcc-c43df9b2e5fd.jpg@!1200" , "https://cdn-usa.skypixel.com/uploads/usa_files/storynode/image/4b32f894-a8e0-4d92-9dc7-f9c5b932723f.JPG@!1200" ] tasks = [] for url in url_list: task = asyncio.create_task(download(url)) tasks.append(task) await asyncio.wait(tasks) if __name__ == '__main__' : loop = asyncio.get_event_loop() loop.run_until_complete(main())