热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:GIL,同步与异步

本文由编程笔记#小编为大家整理,主要介绍了GIL,同步与异步相关的知识,希望对你有一定的参考价值。PythonGIL1、GIL全局解释器锁
本文由编程笔记#小编为大家整理,主要介绍了GIL,同步与异步相关的知识,希望对你有一定的参考价值。

Python GIL

1、GIL全局解释器锁

2、GIL全局解释器锁VS互斥锁

3、定时器

 

1GIL全局解释器锁:

GIL全局解释器锁是一把互斥锁,都是让多个并发线程同一时间只能由一个执行

有了GIL的存在,同一进程内的多个线程同一时间只能有一个在运行,意味着在Cpython中一个进程下多个线程无法实现并行=>意味着无法利用多核优势

但不影响并发的实现

注意:保护不同的数据安全,就应该加不同的锁

为何要用GIL

因为Cpython解释器自带垃圾回收机制,并不是让线程安全的。(原因在于垃圾回收机制会将线程马上要引用值前,回收一个当前并没有绑定的值,线程后面将引用时无法查找到)(Cpython解释器的代码会收到参数,而参数是线程)

如何使用:

GIL是加在Cpython解释器之上,GIL可以比喻成执行权限,同一进程下的所有线程都是先在抢GIL,拿到执行权限,然后将代码交给解释器的代码去执行,保证python解释器同一时间只能执行一个任务的代码

 技术分享图片

2GIL全局解释器锁VS互斥锁

 技术分享图片

GIL保护的是解释器级的数据,保护用户自己的数据则需要自己加锁处理

一切都由操作系统监视,最开始所有线程抢GIL,抢到后如果没有用户设锁,那么遇到IO后,操作系统会将线程的cpu拿走,Cpython解释器释放其GIL,其他线程可以继续抢GIL执行。当前面那个被系统解放GIL的线程再次抢到GIL时,会接着上次被解放的地方继续运行下去。

如果用户有加互斥锁:

那么最开始的时候还是一样都先抢GIL全局解释器锁,抢到的运行到用户的互斥锁地方拿到锁后遇到IO会进行睡眠,此时操作系统会将GIL全局解释器锁释放,别的线程抢到遇到用户的互斥锁没法继续运行下去,因为互斥锁被睡眠的线程拿着,所以他们会被操作系统释放GIL继续抢,一直到睡眠的线程运行结束释放互斥锁后其他抢到GIL全局解释器锁的线程才可以从互斥锁地方运行下去。

(GIL相当于执行权限,会在任务无法执行的情况,被强行释放

自定义互斥锁即便时无法执行,也不会自动释放)

4、有两种并发解决方案:

多进程:计算密集型

多线程:IO密集型

计算密集型

from multiprocessing import Process
from threading import Thread
import os,time
def work1():
res
=0
for i in range(100000):
res
*=i
def work2():
res
=0
for i in range(100000):
res
*=i
def work3():
res
=0
for i in range(100000):
res
*=i
def work4():
res
=0
for i in range(100000):
res
*=i
def work5():
res
=0
for i in range(100000):
res
*=i
def work6():
res
=0
for i in range(100000):
res
*=i
def work7():
res
=0
for i in range(100000):
res
*=i
def work8():
res
=0
for i in range(100000):
res
*=i
if __name__ == __main__:
# print(os.cpu_count()) #本机为4核
start=time.time()
# p1=Process(target=work1)
# p2=Process(target=work2)
# p3=Process(target=work2)
# p4=Process(target=work2)
# p5=Process(target=work2)
# p6=Process(target=work2)
# p7=Process(target=work2)
# p8=Process(target=work2)
p1=Thread(target=work1)
p2
=Thread(target=work2)
p3
=Thread(target=work2)
p4
=Thread(target=work2)
p5
=Thread(target=work2)
p6
=Thread(target=work2)
p7
=Thread(target=work2)
p8
=Thread(target=work2)
p1.start()
p2.start()
p3.start()
p4.start()
p5.start()
p6.start()
p7.start()
p8.start()
p1.join()
p2.join()
p3.join()
p4.join()
p5.join()
p6.join()
p7.join()
p8.join()
stop
=time.time()
print("run time is %s" %(stop-start))
计算密集型 在运算简单情况下线程比进程快,但是程序中的运算都是相对
复杂,所以多进程要比多线程强

IO密集型

 

from multiprocessing import Process
from threading import Thread
import os,time
def work1():
time.sleep(
5)
def work2():
time.sleep(
5)
def work3():
time.sleep(
5)
def work4():
time.sleep(
5)
def work5():
time.sleep(
5)
def work6():
time.sleep(
5)
def work7():
time.sleep(
5)
def work8():
time.sleep(
5)
if __name__ == __main__:
# print(os.cpu_count()) #本机为4核
start=time.time()
p1
=Process(target=work1)
p2
=Process(target=work2)
p3
=Process(target=work2)
p4
=Process(target=work2)
p5
=Process(target=work2)
p6
=Process(target=work2)
p7
=Process(target=work2)
p8
=Process(target=work2)
# p1=Thread(target=work1)
# p2=Thread(target=work2)
# p3=Thread(target=work2)
# p4=Thread(target=work2)
# p5=Thread(target=work2)
# p6=Thread(target=work2)
# p7=Thread(target=work2)
# p8=Thread(target=work2)
p1.start()
p2.start()
p3.start()
p4.start()
p5.start()
p6.start()
p7.start()
p8.start()
p1.join()
p2.join()
p3.join()
p4.join()
p5.join()
p6.join()
p7.join()
p8.join()
stop
=time.time()
print("run time is %s" %(stop-start))

 

定时器

from threading import Timer,current_thread
def task(x):
print("%s run ...." %x)
print(current_thread().name)
if __name__ == __main__:
t
=Timer(3,task,args=(10,))#第一个是时间秒数,第二个是函数名,第三个是传入值
t.start()
print("")

进程优先级别:

import queue
# 队列:先进先出
q=queue.Queue(3)
q.put(
1)
q.put(
2)
q.put(
3)
print(q.get())
print(q.get())
print(q.get())
# 堆栈:先进后出
q=queue.LifoQueue()
q.put(
1)
q.put(
2)
q.put(
3)
print(q.get())
print(q.get())
print(q.get())
# 优先级队列:优先级高先出来,数字越小,优先级越高
q=queue.PriorityQueue()
q.put((
3,data1))
q.put((
-10,data2))
q.put((
11,data3))
print(q.get())
print(q.get())
print(q.get())
打印顺序:
-10
3
11

1、什么时候用池:

池的功能是限制启动的进程数或线程数,

什么时候应该限制

当并发的任务数远远超过了计算机的承受能力时,既无法一次性开启过多的进程数或者线程数时,就应该用池的概念将开启的进程数或者线程数限制在计算机可承受的范围内

2、同步vs异步

同步、异步指的是提交任务的两种方式

同步:提交完任务后就在原地等待,直到任务运行完毕后拿回到任务的返回值,再继续运行下一行代码

异步:提交完任务(绑定一个回调函数)后根本就不在原地等待,直接运行下一行代码,等到任务有返回值后会自动触发回调函数

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import os
import time
import random
def task(n):
print("%s run..." %os.getpid())
time.sleep(
10)
return n**2
def parse(res):
print("...")
if __name__ == __main__:
pool
=ProcessPoolExecutor(9)#进程池默认是4,代表一次性回复个数,
# 超过回复个数,后面谁空谁回复
l=[]
for i in range(1,12):
future
= pool.submit(task,i)
l.append(future)
pool.shutdown(wait
=True)#shutdown关闭进程池入口
for future in l:
print(future.result())
print("")
要运行结束后获得结果,放在父进程中效果不够理想


技术分享图片技术分享图片

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import os
import time
import random
def task(n):
print("%s run..." %os.getpid())
time.sleep(
10)
return n**2
def parse(future):
time.sleep(
1)
res
=future.result()
print("%s 处理了 %s" %(os.getpid(),res))
if __name__ == __main__:
pool
=ProcessPoolExecutor(9)#进程池默认是4,代表一次性回复个数,
# 超过回复个数,后面谁空谁回复
start=time.time()
for i in range(1,12):
future
= pool.submit(task,i)
future.add_done_callback(parse)
#parse会在futrue有返回值时立刻触发,

pool.shutdown(wait
=True)#shutdown关闭进程池入口
stop=time.time()
print("",os.getpid(),(stop-start))


优化进程

技术分享图片技术分享图片

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import os
import time
import random
def task(n):
print("%s run..." %os.getpid())
time.sleep(
10)
return n**2
def parse(future):
time.sleep(
1)
res
=future.result()
print("%s 处理了 %s" %(os.getpid(),res))
if __name__ == __main__:
pool
=ThreadPoolExecutor(9)# 线程池是cpu*5个数,代表一次性回复个数,
# 超过回复个数,后面谁空谁回复
start=time.time()
for i in range(1,12):
future
= pool.submit(task,i)
future.add_done_callback(parse)
#parse会在futrue有返回值时立刻触发,

pool.shutdown(wait
=True)#shutdown关闭进程池入口
stop=time.time()
print("",os.getpid(),(stop-start))


优化线程

通过对比在有IO的情况下,线程要比进程快

 


推荐阅读
  • Flutter 核心技术与混合开发模式深入解析
    本文深入探讨了 Flutter 的核心技术,特别是其混合开发模式,包括统一管理模式和三端分离模式,以及混合栈原理。通过对比不同模式的优缺点,帮助开发者选择最适合项目的混合开发策略。 ... [详细]
  • 高级缩放示例.就像谷歌地图一样.它仅缩放图块,但不缩放整个图像.因此,缩放的瓷砖占据了恒定的记忆,并且不会为大型缩放图像调整大小的图像.对于简化的缩放示例lookhere.在Win ... [详细]
  • 协程作为一种并发设计模式,能有效简化Android平台上的异步代码处理。自Kotlin 1.3版本引入协程以来,这一特性基于其他语言的成熟理念,为开发者提供了新的工具,以增强应用的响应性和效率。 ... [详细]
  • 本文探讨了在UIScrollView上嵌入Webview时遇到的一个常见问题:点击图片放大并返回后,Webview无法立即滑动。我们将分析问题原因,并提供有效的解决方案。 ... [详细]
  • 利用Node.js实现PSD文件的高效切图
    本文介绍了如何通过Node.js及其psd2json模块,快速实现PSD文件的自动化切图过程,以适应项目中频繁的界面更新需求。此方法不仅提高了工作效率,还简化了从设计稿到实际应用的转换流程。 ... [详细]
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • Beetl是一款先进的Java模板引擎,以其丰富的功能、直观的语法、卓越的性能和易于维护的特点著称。它不仅适用于高响应需求的大型网站,也适合功能复杂的CMS管理系统,提供了一种全新的模板开发体验。 ... [详细]
  • MySQL InnoDB 存储引擎索引机制详解
    本文深入探讨了MySQL InnoDB存储引擎中的索引技术,包括索引的基本概念、数据结构与算法、B+树的特性及其在数据库中的应用,以及索引优化策略。 ... [详细]
  • OBS Studio自动化实践:利用脚本批量生成录制场景
    本文探讨了如何利用OBS Studio进行高效录屏,并通过脚本实现场景的自动生成。适合对自动化办公感兴趣的读者。 ... [详细]
  • 解决JavaScript中法语字符排序问题
    在开发一个使用JavaScript、HTML和CSS的Web应用时,遇到从SQLite数据库中提取的法语词汇排序不正确的问题,特别是带重音符号的字母未按预期排序。 ... [详细]
  • 3.[15]Writeaprogramtolistallofthekeysandvaluesin%ENV.PrinttheresultsintwocolumnsinASCIIbet ... [详细]
  • 如何高效解决Android应用ANR问题?
    本文介绍了ANR(应用程序无响应)的基本概念、常见原因及其解决方案,并提供了实用的工具和技巧帮助开发者快速定位和解决ANR问题,提高应用的用户体验。 ... [详细]
  • 本文探讨了Java中线程的多种终止方式及其状态转换,提供了关于如何安全有效地终止线程的指导。 ... [详细]
  • 小米路由器AX6000与小米11同步推出,不仅在硬件配置上达到了旗舰级水准,其独特的4K QAM技术更是引领了行业新标准。本文将深入探讨这款路由器的性能表现及其实际应用。 ... [详细]
  • 本文将深入探讨 Unreal Engine 4 (UE4) 中的距离场技术,包括其原理、实现细节以及在渲染中的应用。距离场技术在现代游戏引擎中用于提高光照和阴影的效果,尤其是在处理复杂几何形状时。文章将结合具体代码示例,帮助读者更好地理解和应用这一技术。 ... [详细]
author-avatar
05358
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有