day37

#######################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("提交完成!")

"""

"""

 

上一篇:36 线程2....GIL


下一篇:第三十章:GIL和线程池,进程池