#######################GIL锁
GIL的优点:
-
保证了CPython中的内存管理是线程安全的
GIL的缺点:
-
互斥锁的特性使得多线程无法并行
在 Cpython中,这个全局解释器锁 或者 称为GIL,是一个互斥锁. 是为了防止多个本地线程同一时间执行python字节码,
这个锁是非常重要的因为Cpython的内存管理是非线程安全的, ,然而这个GIL有存在的必要性, 因为有很多已经存在的代码,需要依赖这个锁
非线程安全 即 多个线程访问同一个资源,会有有问题
线程安全 即 多个线程访问同一个资源,不会有问题
CPython中有一个互斥锁,防止线程同一时间执行python代码
该锁只存在Cpython中,这并不是Python这们语言的 除了Cpython之外 Jpython, pypy,解释器
之所以使用Cpython的原因??
C编译过的结果可以计算机直接识别
最主要的语言,C语言以后大量现成的,库(算法,通讯),Cpython可以无缝连接C语言的任何现成代码
python 胶水 就是这也能干 那也能干
#######垃圾回收机制
python中不需要手动管理内存
当垃圾回收启动后会将计数为0的数据清除掉,回收内存
分代回收
自动垃圾回收其实就是说,内部会有一个垃圾回收线程,会在某一时间运行起来,开始清理垃圾
这是可能会产生问题,例如线程1申请了内存,但是还没有使用CPU切换到了GC,GC将数据当成垃圾清理掉了
为了解决这个问题,Cpython就给解释器加上了互斥锁!
############GIL锁的加锁与解锁时机
加锁: 只有有一个线程要使用解释器就立马枷锁
释放:
该线程任务结束
该线程遇到IO
该线程使用解释器过长 默认100纳秒
#######GIL给我们造成的影响
多线程不能并行
案例:
有一个下载任务 要从网络中下载一个文件 大小1G
和转换 任务 使用input 转为大写输出
上述任务并行执行,耗时也不会有太大的提升,反而开启多进程会浪费更多资源
这种任务称之为IO密集型,大量的时间都花在IO等待
这类问题使用多线程,
计算密集型任务
图像处理,语音处理,大数据分析,
##########解决方案
区分任务类型
如果是IO密集使用多线程
如果是计算密集使用多进程
#################多进程,多线程
如果是IO密集使用多线程
如果是计算密集使用多进程
###########GIL锁与自定义锁的关系
都是互斥锁
为什么有了GIL还需要自己加锁
GIL是加在解释器上的,只能锁住,解释器内部的资源,但是无法锁住我们自己开启资源
例如: 我们自己开启了一个json文件,多个线程要同时访问, 如果第一个线程写入数据写到一半,执行超时了,另一个线程过来接着写,就会产生问题,
自己开启共享资源还得自己所锁
像是申请内存 保存数据等等就不需要我们程序员考虑了 GIL已经搞定了
#############线程池与进程池
线程池
线程池,不仅帮我们管理了线程的开启和销毁,还帮我们管理任务的分配
特点: 线程池中的线程只要开启之后 即使任务结束也不会立即结束 因为后续可能会有新任务
避免了频繁开启和销毁线程造成的资源浪费
1.创建一个线程池
2.使用submit提交任务到池子中 ,线程池会自己为任务分配线程
池就是容器,
线程池就是装线程的容器
好处:
1.自动管理线程的开启和销毁
2.自动分配任务给空闲的线程
3.可以线程开启线程的数量 保证系统稳定
信号量中是限制同时并发多少,但是线程已经全都建完了
如何使用:
1.创建池子 pool = ThreadPoolExecutor(3)
2.submit 提交任务
3.pool.shutdown() # 等待所有任务全部完毕 销毁所有线程 后关闭线程池
关闭后就不能提交新任务了
#################同步异步
在并发中 经常提及的几个概念
阻塞 - 非阻塞 程序的状态
程序的运行状态 非阻塞 可能就绪 或者 运行
并发 - 并行 多任务处理方式
多个任务看起来像是同时运行 ,本质是切换+保存状态
并行真正的同时进行中, 必须具备多核处理器
同步 异步 任务提交(执行方式)方式
同步 同步==阻塞 × 卡住 == 阻塞 ×
指的是发起任务后,必须在原地等待,直到任务完成拿到结果 ,称之为同步
默认情况就是同步的
异步 异步 == 非阻塞 ×
发起任务,不需要等待结果,可以继续执行其他代码,异步
异步任务必须依赖,并发或并行, 在python中 通过多线程或多进程
异步的效率明显高于同步
pool = ThreadPoolExecutor() # my cpu_count = 6 so 6 * 5 = 30
def task():
time.sleep(2)
print("执行完成!")
return "一瓶黑牛!"
print("start")
# task()# 同步调用
# 1.发起一个异步任务
res = pool.submit(task) # 异步调用 res就是一个表示异步任务的对象
# 2.定义一个回调函数 传的参数就是一个完成后任务对象
def finished(arg):
print(arg.result())
print("黑牛买回来了! ")
pass
# 3.给这个异步任务添加了一个回调函数
res.add_done_callback(finished)
# pool.shutdown() # 阻塞直到线程池所有任务全部完成 会导致主线卡在原地
# print(res)
# print("执行的结果:",res.result()) # 会导致主线卡在原地
print("over")
################异步回调
"""
爬虫是干啥的
从网络中下载某个页面数据
在从页面中提取有价值的数据
"""
import requests
from concurrent.futures import ThreadPoolExecutor
from threading import current_thread
pool = ThreadPoolExecutor()
# 获取数据
def get_data(url):
response = requests.get(url)
# return url,response.text
print("%s下载完成!" % current_thread().name)
parser(url,response.text)
# 解析数据
def parser(url,data):
# url,data = obj.result()
print("%s 长度%s" % (url, len(data)))
print("%s解析完成!" % current_thread().name)
urls = [
"http://www.baidu.com",
"http://www.qq.com",
"http://www.python.org",
"http://www.sina.com",
"http://www.oldboyedu.com",
]
"""
为嘛使用异步回调
1.生产者和消费者解开了耦合
2.消费者可以及时处理生产完成的数据
"""
for u in urls:
obj = pool.submit(get_data,u)
# obj.add_done_callback(parser)
print("提交完成!")
"""
"""