CIL锁,GIL与线程池的区别,进程池和线程池,同步与异步
一.GIL锁
什么是GIL? 全局解释器锁,是加在解释器上的互斥锁
GC是python自带的内存管理机制,GC的工作原理:python中的内存管理使用的是应用计数,每个数会被加上一个整型的计数器,表示这个数据被引用的次数,当这个整数变为0时则表示该数据已经没有人使用,成为了垃圾数据,当内存占用达到某个阈值,GC会将其他线程挂起,然后执行垃圾清理操作,垃圾清理也是一串代码,也就需要一条线程来执行.
为什么需要GIL?
由于CPython的内存管理机制是非线程安全,于是CPython就给解释器加了一个锁,解决了安全问题,但是降低了效率,另外,虽然有解决方案,但是由于牵涉太多,一旦修改则很多基于GIL的程序都需要修改,所以变成了历史遗留问题.
GIL加锁,解锁的时机?
加锁时机:在调用解释器时立即加锁
解锁时机:①当前线程遇到IO时释放 ②当前线程执行时间超过设定值时释放,解释器会检测线程的执行时间,一旦到达某个阈值,通知线程保存状态切换线程.
GIL带来的问题:即使是多核处理器下也无法真正的并行.
总结:
①在单核情况下,无论是IO密集型还是计算密集型,GIL都不会产生影响,而多线程开销小,并且节约资源,所以使用多线程.
②在多核情况下,IO密集型会受到GIL的影响,但是很明显IO速度远比计算速度慢,所以两者执行的时间差不多,基本可以忽略不计,而在这个情况下我们考虑到多线程开销小,并且节约资源,所以多核情况下,IO密集型我们使用多线程.
③对于计算密集型,在多核情况下,CPython中多线程是无法并行的,为了解决这一弊端,Python推出了多进程技术,可以良好的利用多核处理器来完成计算的任务.
多线程用于IO密集型,如socket,爬虫,web
多进程用于计算密集型,如金融分析
多进程与多线程效率对比:
现在的电脑都是多核系统
#多进程解决计算密集型
from multiprocessing import Process
import time
a = 10
def task():
for i in range(10000000):
global a
a +=1
a*10/2-3
if __name__ == '__main__':
start = time.time()
ps = []
for i in range(3):
p = Process(target=task)
p.start()
ps.append(p)
for p in ps:
p.join()
print(time.time()-start)
结果:5.455920934677124 #多线程解决计算密集型
from threading import Thread
import time
a = 10
def task():
for i in range(10000000):
global a
a +=1
a*10/2-3
if __name__ == '__main__':
start = time.time()
ts = []
for i in range(3):
t = Thread(target=task)
t.start()
ts.append(t)
for t in ts:
t.join()
print(time.time()-start)
结果:8.375339031219482 #多进程解决IO密集型
from multiprocessing import Process
import time
def task():
path =r'E:\python试学视频\day27、28选课系统\11 测试程序2.mp4'
with open(path,mode='rb') as f:
while True:
data = f.read(1024)
if not data:
break
if __name__ == '__main__':
start = time.time()
ps = []
for i in range(3):
p = Process(target=task)
p.start()
ps.append(p)
for p in ps:
p.join()
print(time.time()-start)
结果:0.3124856948852539
#多线程解决IO密集型
from threading import Thread
import time
a = 10
def task():
path =r'E:\python试学视频\day27、28选课系统\11 测试程序2.mp4'
with open(path,mode='rb') as f:
while True:
data = f.read(1024)
if not data:
break
if __name__ == '__main__':
start = time.time()
ts = []
for i in range(3):
t = Thread(target=task)
t.start()
ts.append(t)
for t in ts:
t.join()
print(time.time()-start)
结果:0.1250016689300537
二.GIL锁与自定义锁的区别
GIL是用于保护解释器相关的数据,解释器也是一段程序,肯定有其定义的各种数据
GIL并不能保证自己定义的数据的安全,所以当程序中出现多线程共享数据的时候就需要自定义加锁.
三.线程池与进程池
什么是进程池/线程池?
池表示是一个容器,本质就是一个存储进程或线程的列表
IO密集型使用线程池,计算密集型使用进程池
为什么需要线程池/进程池?
很多情况下需要控制进程或者线程在一个合理的范围内,线程/进程池不仅帮我们控制线程/进程的数量,还帮我们完成了线程/进程的创建,销毁,以及任务的分配
线程池的使用:
from concurrent.futures import ThreadPoolExecutor
from threading import current_thread,active_count
import time
#创建线程池,指定最大线程数为3 如果不指定 默认为cpu核心数*5
pool = ThreadPoolExecutor(3) #不会立即开启子线程
def task():
print('%s running..'%current_thread().name)
print(active_count())
time.sleep(2)
#提交任务到线程池
for i in range(10):
pool.submit(task)
进程池的使用:
from concurrent.futures import ProcessPoolExecutor
import time,os
#创建进程池,最大进程数为3,默认为cpu个数
pool = ProcessPoolExecutor(3)#不会立即开启子进程
def task():
print('%s running..'%os.getpid())
time.sleep(2)
if __name__ == '__main__':
#提交任务到进程池
for i in range(10):
pool.submit(task)
#第一次提交任务时会创建进程后续提交任务直接交给已经存在的进程来完成,如果没有空闲进程就等待
结果:
1464 running..
11732 running..
8236 running.. 1464 running..
11732 running..
8236 running.. 1464 running..
11732 running..
8236 running.. 1464 running..
案例:TCP中的应用
首先要明确,TCP是IO密集型,应该使用线程池
#多线程TCP服务器
from concurrent.futures import ThreadPoolExecutor
import socket
server = socket.socket()
server.bind(('192.168.12.207',4396))
server.listen()
pool = ThreadPoolExecutor(3) #线程池,控制可以连接到服务器的客户端的个数
def task(client):
while True:
try:
data = client.recv(1024)
if not data:
client.close()
break
client.send(data.upper())
except ConnectionResetError:
client.close()
break
while True:
client,addr = server.accept()
t = pool.submit(task,client)
#多线程TCP客户端
#使用多线程是为了可以一直输入,不用等输出了才可以输入
from threading import Thread
import socket
client = socket.socket()
client.connect(('192.168.12.207',4396))
def send_msg():
while True:
msg = input('>>:').strip()
if not msg:
continue
client.send(msg.encode('utf-8'))
send_t = Thread(target=send_msg)
send_t.start()
while True:
try: #这个也要自定义抛出异常,如果服务器终止,客户端也会报错
data = client.recv(1024)
print(data.decode('utf-8'))
except:
client.close()
break
与信号量的区别:
信号量也是一种锁,适用于保证同一时间能有多少个进程或线程访问
而线程池和进程池,没有对数据访问进行限制仅仅是控制数量
四.同步与异步
同步(调用/执行/任务/提交),发起任务后必须等待任务结束,拿到一个结果才能继续运行
异步 发起任务后不需要关系任务的执行过程,可以继续往下运行,但还是需要结果
异步效率高于同步但是并不是所有任务都可以异步执行,判断一个任务是否可以异步的条件是,任务发起方是否立即需要执行结果
同步不等于阻塞 异步不等于非阻塞当使用异步方式发起任务时 任务中可能包含io操作 异步也可能阻塞同步提交任务 也会卡主程序 但是不等同阻塞,因为任务中可能在做一些计算任务,CPU没有切换到其他程序
from concurrent.futures import ThreadPoolExecutor
import time
pool = ThreadPoolExecutor()
def task():
time.sleep(1)
print('sub thread run...')
for i in range(10):
pool.submit(task) #submit是以异步的方式提交任务
print('over')
from concurrent.futures import ThreadPoolExecutor
import time
pool = ThreadPoolExecutor()
def task(i):
time.sleep(1)
print('sub thread run ...')
i += 1
return i
for i in range(10):
f = pool.submit(task,i)
print(f)
print(f.result()) #result是阻塞的,会等到这个任务执行完毕才能继续执行,会将异步变为同步
print('over')
#同步又变为了异步
from concurrent.futures import ThreadPoolExecutor
import time
pool = ThreadPoolExecutor()
def task(i):
time.sleep(1)
print('sub thread run ...')
i += 1
return i
fs = []
for i in range(10):
f = pool.submit(task,i)
fs.append(f)
#是一个阻塞函数,会等到池子中的所有任务完成后继续执行
pool.shutdown() #里面有一个wait参数:默认值是True
#注意:shutdown之后就不能提交新任务了
for i in fs:
print(i.result())
print('over')
CIL锁,GIL与线程池的区别,进程池和线程池,同步与异步的更多相关文章
- Java进程和线程关系及区别
1.定义 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基 ...
- python笔记-10(socket提升、paramiko、线程、进程、协程、同步IO、异步IO)
一.socket提升 1.熟悉socket.socket()中的省略部分 socket.socket(AF.INET,socket.SOCK_STREAM) 2.send与recv发送大文件时对于黏包 ...
- Python之路(第四十一篇)线程概念、线程背景、线程特点、threading模块、开启线程的方式
一.线程 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是 ...
- java中的线程问题(一)什么是线程。
线程--什么是进程 进程--概念 要解释线程,就必须明白什么是进程. 什么是进程呢? 进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间),比如用户点击桌面的IE浏览器,就启动了一个进 ...
- 并发编程: GIL锁、GIL与互斥锁区别、进程池与线程池的区别
一.GIL 二.关于GIL性能的讨论 三.计算密集测试 四.IO密集测试 五.GIL与互斥锁 六.TCP客户端 七.进程池 八.进程什么时候算是空闲 九.线程池 一.GIL GIL Global In ...
- -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中
本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait( ...
- python 线程队列、线程池、全局解释器锁GIL
一.线程队列 队列特性:取一个值少一个,只能取一次,没有值的时候会阻塞,队列满了,也会阻塞 queue队列 :使用import queue,用法与进程Queue一样 queue is especial ...
- GIL全局解释器锁+GIL全局解释器锁vs互斥锁+定时器+线程queue+进程池与线程池(同步与异步)
以多线程为例写个互斥锁 from threading import Thread ,Lockimport timemutex = Lock() n = 100 def task(): global n ...
- Python进阶----GIL锁,验证Cpython效率(单核,多核(计算密集型,IO密集型)),线程池,进程池
day35 一丶GIL锁 什么是GIL锁: 存在Cpython解释器,全名:全局解释器锁.(解释器级别的锁) GIL是一把互斥锁,将并发运行变成串行. 在同一个进程下开启的多个线 ...
随机推荐
- Tortoisegit和GitHub使用入门
作为一个Code人对于github并不陌生吧,记录下使用说明: gitHub是一个面向开源及私有软件项目的托管平台,因为只支持git 作为唯一的版本库格式进行托管,故名gitHub. 工具: git: ...
- 第六天实验详解——dedecms通过xss漏洞写马
第六天实验详解 **XSS跨站攻击的分类** XSS漏洞类型主要分为持久型和非持久型两种: 1. 非持久型XSS漏洞一般存在于URL参数中,需要访问黑客构造好的特定URL才能触发漏洞. 2. 持久型X ...
- 学习笔记: ES7(ES2016)新功能
ES7添加了两个新功能 : 1. Array.prototype.includes() 2. 指数运算符 1 .Array.prototype,includes() 判断指定的元素是否存在于数组中, ...
- python爬取12306及各参数的使用。完整代码
import requestsfrom retrying import retryreuquests和retrying的下载及安装可以通过命令行pip install 口令实现 # 调用重连装饰器固定 ...
- faces
install Boost [boost_1_65_1-msvc-14.0-32.exe]BOOST_LIBRARYDIR=D:\_softwares_kits\boost_1_65_1\lib32- ...
- python base64加密文本内容(2)
上面一篇简单进行了base64加密 但安全系数非常低,只需要用网上的在线base64解码就能破解 这里我们可以自己修改一下base64加密后的字符串,在解码的时候只需要先提前将修改的内容还原就可以了 ...
- 详解tween.js 中文使用指南
补间(动画)是一个概念,允许你以平滑的方式更改对象的属性.你只需告诉它哪些属性要更改,当补间结束运行时它们应该具有哪些最终值,以及这需要多长时间,补间引擎将负责计算从起始点到结束点的值. 例如,pos ...
- html5-浮动
#div1{ background: rgba(255,0,0,0.5); width: 250px; height: 250px; float: right;}#div2{ ...
- EasyUI表格DataGrid获取数据的方式
第一种方式:直接返回JSON数据 package com.easyuijson.util; import java.text.SimpleDateFormat; import net.sf.js ...
- django之auth认证系统
Django自带的用户认证 我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统.此时我们需要实现包括用户注册.用户登录.用户认证.注销.修改密码等功能,这还真是个麻烦的事情呢. Djang ...