初学python之路-day37

1.GIL锁:

Global Interpreter Lock  全局解释器锁

在 Cpython中,这个全局解释器锁 或者 称为GIL,是一个互斥锁. 是为了防止多个本地
线程同一时间执行python字节码, 这个锁是非常重要的因为Cpython的内存管理是非线程安全的, ,然而这个GIL有存在的
必要性, 因为有很多已经存在的代码,需要依赖这个锁 非线程安全 即 多个线程访问同一个资源,会有有问题 线程安全 即 多个线程访问同一个资源,不会有问题 CPython中有一个互斥锁,防止线程同一时间执行python代码 ,该锁只存在Cpython中 之所以使用Cpython的原因?? C编译过的结果可以计算机直接识别 最主要的原因,C语言以后大量现成的,库(算法,通讯),Cpython可以无缝连接C语言的任
何现成代码

2.内存管理与GIL锁

由于垃圾回收机制 ,python中不需要手动管理内存

当垃圾回收启动后会将计数为0的数据清除掉,回收内存
自动垃圾回收其实就是说,内部会有一个垃圾回收线程,会在某一时间运行起来,开始清理
垃圾 这是可能会产生问题,例如线程1申请了内存,但是还没有使用CPU切换到了GC,GC将数据
当成垃圾清理掉了 为了解决这个问题,Cpython就给解释器加上了GIL互斥锁!
为何给Cpython解释器加上GIL锁
Cpython诞生于1991年  而多核处理器诞生2004年  
当时不需要考虑多核效率问题  
现在为什么不拿掉这个锁,因为这期间,很多已经完成的代码都依赖于这个锁,如果直接换
掉,这些代码全得改,成本太大了

3.GIL锁的运用

GIL锁的加锁与解锁时机:
加锁: 只有有一个线程要使用解释器就立马枷锁  
解锁:1该线程任务结束

​    2该线程遇到IO

​    3该线程使用解释器过长    默认100纳秒

影响:
由于互斥锁的特性,程序串行,保证数据安全,降低执行效率,GIL将使得程序整体效率
降低! 解决方法: 区分任务类型: 1如果是IO密集使用多线程 2如果是计算密集使用多进程
GIL锁与自定义锁的关系:
GIL是加在解释器上的,只能锁住,解释器内部的资源,但是无法锁住我们自己开启资源 
例如: 我们自己开启了一个json文件,多个线程要同时访问, 如果第一个线程写入数据
写到一半,执行超时了,另一个线程过来接着写,就会产生问题, 自己开启共享资源还得自己所锁 像是申请内存 保存数据等等就不需要我们程序员考虑了 GIL已经搞定了

4.线程池与进程池

池 Pool 指得是一个容器
线程池就是用来存储线程对象的 容器
好处:
1.自动管理线程的开启和销毁 
2.自动分配任务给空闲的线程
3.可以线程开启线程的数量 保证系统稳定     
信号量中是限制同时并发多少,但是线程已经全都建完了

使用:
from concurrent.futures import  ThreadPoolExecutor,ProcessPoolExecutor
from threading import enumerate,current_thread

# 1.创建池子 可以指定池子里有多少线程    如果不指定默认为CPU个数 * 5
# 不会立即开启线程 会等到有任务提交后在开启线程
# pool = ThreadPoolExecutor(10)   # 线程池最大值,应该机器所能承受的
最大值 当然需要考虑你的机器有几个任务要做
# # # print(enumerate()) # def task(name,age): # print(name) # print(current_thread().name,"run") # time.sleep(2) # # # # # 该函数提交任务到线程池中 # pool.submit(task,"jerry",10) #任务的参数 直接写到后面不需要定义
参数名称 因为是可变位置参数
# pool.submit(task,"owen",20) # pool.submit(task) # # time.sleep(5) # # # # print(enumerate()) # # # """ 线程池,不仅帮我们管理了线程的开启和销毁,还帮我们管理任务的分配 特点: 线程池中的线程只要开启之后 即使任务结束也不会立即结束 因为后续可能
会有新任务 避免了频繁开启和销毁线程造成的资源浪费 1.创建一个线程池 2.使用submit提交任务到池子中 ,线程池会自己为任务分配线程
""" # 进程池的使用 同样可以设置最大进程数量 默认为cpu的个数 pool = ProcessPoolExecutor() # 具体的值也要参考机器性能 def task(name): print(os.getpid()) print(name) if __name__ == '__main__': pool.submit(task,"jerry") pool.shutdown()
 # 等待所有任务全部完毕 销毁所有线程 后关闭线程池 pool.submit(task,
"jerry")

5.同步异步

同步异步-阻塞非阻塞:
#### 阻塞非阻塞指的是程序的运行状态
阻塞:当程序执行过程中遇到了IO操作,在执行IO操作时,程序无法继续执行其他代码,
称为阻塞! 非阻塞:程序在正常运行没有遇到IO操作,或者通过某种方式使程序即时遇到了也不会停
在原地,还可以执行其他操作,以提高CPU的占用率
####同步-异步 指的是提交任务的方式 同步指调用:发起任务后必须在原地等待任务执行完成,才能继续执行 异步指调用:发起任务后必须不用等待任务执行,可以立即开启执行其他操作 **同步会有等待的效果但是这和阻塞是完全不同的,阻塞时程序会被剥夺CPU执行权,
而同步调用则不会!**
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time
from concurrent.futures._base import Future

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")

6.异步回调

异步回调指的是:在发起一个异步任务的同时指定一个函数,在异步任务完成时会自
动的调用这个函数
### 为什么需要异步回调 之前在使用线程池或进程池提交任务时,如果想要处理任务的执行结果则必须调
用result函数或是shutdown函数,而它们都是是阻塞的,会等到任务执行完毕后才
能继续执行,这样一来在这个等待过程中就无法执行其他任务,降低了效率,所以需要
一种方案,即保证解析结果的线程不用等待,又能保证数据能够及时被解析,该方案就
是异步回调
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("提交完成!")
import requests
from concurrent.futures import ThreadPoolExecutor
from threading import current_thread

pool = ThreadPoolExecutor(2)

# 获取数据
def get_data(url):
    response = requests.get(url)
    print("%s下载完成!" % current_thread().name)
    return url,response.text


# 解析数据
def parser(obj):
    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("提交完成!")

猜你喜欢

转载自www.cnblogs.com/wangwei5979/p/10982315.html
今日推荐