并发编程

并发编程

并发:假的多任务

并行:真的多任务

多线程threading,利用cpu和i/o 可以同时执行的原理,让CPU不在等待i/o完成

多进程multiprocessing,利用多核CPU的能力,真正的平行执行任务

异步i/o asyncio ,在单线程利用CPU和io同时执行的原理,实现函数异步执行

使用lock对资源加锁,防止冲突访问

使用queue实现不同线程、进程之间的数据通信,实现生产者和消费者模型

使用线程池Pool,进程池Pool。简化线程进程的任务提交,等待结果,获取结果。

使用subprocess启动外部程序的进程,并进行输入输出交互。

CPU密集型:

  是i/o在很短的时间内可以完成,CPU需要大量的计算和处理,特点是CPU的利用率相当的高

  压缩和解压缩,加密解密,正则表达式等

i/o密集型:

  是系统运作大部分的状况是CPU在等i/o的读写操作,CPU的利用率低

  文件处理程序,网络爬虫程序,读写数据库程序

多线程Thread

优点:相比进程,更加轻量级,占用资源少

缺点:相对进程,多线程只能并发执行,不能利用多cpu(GIL)

   相比协程,启动数目有限,占用资源,有线程切换开销

适用于:i/o密集型计算,同时运行的任务数目要求不多

多线程数据通信的queue.Queue

queue.Queue可以用于多线程之间的,线程安全的数据通信

1. 引入类库

import  queue

2,创建Queue

q = queue.Queue()

3.添加元素

q.put(item)

4.获取元素

item = q.get()

5.查询状态

  q.qsize()  #用来判断元素的多少

  q.empty()  #用来判断是否为空

  q.full()  # 判断是否已满 

lock 用于解决线程安全问题

用法一:try——finally模式

import threading

lock = threading.Lock()

lock.acquire()

try:

  #do something

finally:  

  lock.release()

用法二:with模式

import threading

lock = threading.Lock()

with lock:

  #do something

线程池

优点:

1. 提升性能:因为减去了大量新建,终止线程的开销,重用了线程的资源:

2. 适用场景:事和处理突发性大量请求或者需要大量线程完成任务,但实际任务处理时间较短

3. 防御功能:有效避免系统因为创建线程过多,而导致的负荷过大相应变慢的问题

4. 代码优势:使用线程池的语法比自己兴建线程执行更加简洁

线程的生命周期

ThreadPoolExecutor的使用语法

方式一:map函数,很简单

注意:map的结果和入参是顺序对应的

from  concurrent.future import ThreadPoolExecutor,as_completad

with ThreadPoolExecutor() as pool:

  results = pool.map(craw,urls)  #urls 为参数列表

  for  result in results:

    print(result)

方式二:future模式,更强大

注意:如果使用as_completed顺序是不定的

from  concurrent.future import ThreadPoolExecutor,as_completad

with ThreadPoolExecutor() as pool:

  futures = [pool。submit(crawl,url  for url in urls]

  for future in futures:  # 有序返回

    print(future.result())  

  for future in as_completed(futures): # 无序返回

    print(future.result())

使用线程池改造程序

多进程process

优点:可以利用多核CPU并行运算

缺点:占用资源最多,可启用的数目比线程少

适用于:CPU密集型计算

注意点:

多协程Corutine

优点:内存开销最少,启动的线程数目多

缺点:支持的库有限(aiohttp vs requests)代码实现复杂

适用于:i/o密集型计算,需要超多任务运行,但有现成库支持的场景

GIL(全局解释器锁)

  是计算机编程语言解释器用于同步线程的一种机制,它使得任何时可仅有一个行程在执行,即使在多核心处理器上,使用GIL的解释器也只允许同一时间执行一个线程。(保证引用计数器的安全)

 Python  异步i/o库介绍:asyncio

注意:

要用在异步i/o编程中

依赖的库必须支持异步i/o特性

爬虫引用中:

requests  不支持异步,需要用aiohttp

import asyncio

#获取事件循环

loop =  asyncio.get_event_lppp()

#定义协程

saync def myfunc():

  await get_url(url)

# 创建task列表

tasks = [loop.create_task(myfunc(url) for  url  in  urls]

# 执行爬虫事件列表

loop.run_until_complete(asyncio.wait(tasks))

 信号量(英语:Semaphore)

信号量,又称为信号量。旗语是一个同步对象,用于保持在0到指定最大值之间的一个数值

 1. 当线程完成一次对该semaphore对象的等待(wait)时,该计数值减一。

 2. 当线程完成一次对semaphore对象的释放(release)时,计数值加一。

 3. 当计数值为0 ,测线程等待该semaphore对象不再能成功直到该semaphore对象变成signaled状态

 4. semaphore对象的计数值大于0 ,为signaled状态,计数值等于0,为nonsignaled状态。

使用方法一:

sem = asynciko。Semaphore(10)

@  ...later

async with sem:

# work with shared  resource

使用方法二

sem = asyncio.Semaphore(10)

#  ....later

await  sam.acquire()

try:

  # work with shared resource

finally:

  sem.release()

 使用subprocess启动电脑的子进程

 subptoces 模块:

允许你生成新的进程

连接它们的输入,输出,错误管道

并且获取它们的返回值

 应用场景

每天定时8:00自动打开酷狗音乐播放歌曲

调用7z。exe自动解压缩.7z文件

通过Python远程提交一个torrent种子文件,用电脑启动下载

好好学习,天天向上。
原文地址:https://www.cnblogs.com/f211/p/14975008.html