python并发编程笔记2

python并发编程笔记2

是根据蚂蚁学Python的视频做的笔记,方便自己后续回顾

视频链接:BV1bK411A7tV

老师的源码

这一份笔记对应的是视频的P3-P4

P3-Python速度慢的罪魁祸首,全局解释器锁GIL

1、Python速度慢的两大原因

  • 原因1:

    动态类型语言:

    ​ Python的变量既可以是数字,也可以随时切换成字符串,导致运行时随时检测类型

    边解释边执行:

    ​ C语言直接编译成机器码,直接运行非常快,而Python执行就是源码,存在一个源码到机器码的翻译过程

  • 原因2:

    GIL:

    ​ 它的存在,使Python无法利用多核CPU并发执行

2、GIL是什么?

全局解释器锁〔英语:Global Interpreter Lock,缩写GIL)

是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行。

即便在多核心处理器上,使用GIL的解释器也只允许同一时间执行一个线程。

下图就是一个例子:

三个线程Thread1、2、3,哪个线程持有GIL,该线程才在执行,当遇到I/O时,就会释放GIL,I/O是指读写操作、发送网络等等

Thread1持有GIL,遇到I/O,释放GIL,这时Thread2才能开始进入运行状态

在这里插入图片描述

由于GIL的存在,即使电脑有多核CPU单个时刻也只能使用1个,相比并发加速的C++/JAVA所以慢。

单个时刻单个进程只能使用1个线程,但多进程下就可以有。

总结:(Python是一门伪多线程语言)

3、为什么有GIL这个东西?

GIL存在目的:为了解决多线程之间数据完整性和状态同步问题

GIL存在原因:Python中对象的管理,是使用引用计数器进行的,引用数为0则释放对象**(涉及到Python的垃圾回收)**

举例:开始:线程A和线程B都引用了对象obj,obj.ref_num 2,线程A和B都想撤销对obj的引用

若其中发生多线程调度切换,进行了两次对象释放,其导致的结果:发生报错,可能错释放别的内存,破坏内存。
在这里插入图片描述

GIL的引入就是解决以上类似的问题:简化了Python对共享资源的管理

4、怎样规避GIL带来的限制?

1、多线程threading用于IO密集型计算,不用于CPU密集型计算

因为在I/O (read,write,send,recv,etc.)期间,线程会释放GIL,实现CPU和IO的并行因此多线程用于IO密集型计算依然可以大幅提升速度

用于CPU密集型计算时,只会更加拖慢速度(只有一个cpu在运行,切多线程切换会增加开销)

2、使用multiprocessing的多进程机制实现并行计算、利用多核CPU优势

为了应对GIL的问题,Python提供了multiprocessing

P4-使用多线程,Python爬虫被加速10倍

1、Python创建多线程的方法

1、准备一个函数

def my_func(a, b):
    do_craw(a, b)

2、怎样创建一个线程

import threading
t = threading.Thread(target=my_func, args=(100, 200))

3、启动线程

t.start()

4、等待结束

t.join()

2、改写爬虫程序,变成多线程爬取

# blog_spider.py

import requests

# 注意这里不要使用老师提供的这个(可能加入了防爬),否则你后面的爬取的永远只有第一页的数据
# urls = [f"https://www.cnblogs.com/#p{page}" for page in range(1, 51)]
# 请将urls改成这个
urls = [
    f"https://www.cnblogs.com/sitehome/p/{page}"
    for page in range(1, 50 + 1)
]


def craw(url):
    r = requests.get(url)
    print(url, len(r.text))

# craw(urls[0])

# 01multi_thread_craw.py

import blog_spider
import threading
import time


# 单线程代码
def single_thread():
    print("single_thread begin")
    for url in blog_spider.urls:
        blog_spider.craw(url)
    print("single_thread end")


# 多线程代码
def multi_thread():
    print("multi_thread begin")
    # 创建一个空列表,将每一个创建好的线程对象存进这个列表当中
    threads = []
    # 对每一个URL创建一个线程
    for url in blog_spider.urls:
        threads.append(
            threading.Thread(target=blog_spider.craw, args=(url,))
        )
    # 对每个线程进行启动
    for thread in threads:  
        thread.start()

    for thread in threads:
        thread.join()

    print("multi_thread end")


if __name__ == '__main__':
    start = time.time()
    single_thread()
    end = time.time()
    print("single_thread cost:", end - start, "seconds")

    start = time.time()
    multi_thread()
    end = time.time()
    print("multi_thread cost:", end - start, "seconds")

3、速度对比:单线程爬虫VS多线程爬虫

single_thread cost: 8.126458168029785 seconds

multi_thread cost: 0.3084845542907715 seconds

8.12/3
2.7066666666666666

d cost:", end - start, “seconds”)




## 3、速度对比:单线程爬虫VS多线程爬虫

single_thread cost: 8.126458168029785 seconds

multi_thread cost: 0.3084845542907715 seconds

8.12/3
2.7066666666666666


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值