day34 GIL锁,线程队列,线程池
一.GIL锁(Global Interpreter Lock)
首先,一些语言(java、c++、c)是支持同一个进程中的多个线程是可以应用多核CPU的,也就是我们会听到的现在4核8核这种多核CPU技术的牛逼之处。那么我们之前说过应用多进程的时候如果有共享数据是不是会出现数据不安全的问题啊,就是多个进程同时一个文件中去抢这个数据,大家都把这个数据改了,但是还没来得及去更新到原来的文件中,就被其他进程也计算了,导致数据不安全的问题啊,所以我们是不是通过加锁可以解决啊,多线程大家想一下是不是一样的,并发执行就是有这个问题。但是python最早期的时候对于多线程也加锁,但是python比较极端的(在当时电脑cpu确实只有1核)加了一个GIL全局解释锁,是解释器级别的,锁的是整个线程,而不是线程里面的某些数据操作,每次只能有一个线程使用cpu,也就说多线程用不了多核,但是他不是python语言的问题,是CPython解释器的特性,如果用Jpython解释器是没有这个问题的,Cpython是默认的,因为速度快,Jpython是java开发的,在Cpython里面就是没办法用多核,这是python的弊病,历史问题,虽然众多python团队的大神在致力于改变这个情况,但是暂没有解决。(这和解释型语言(python,php)和编译型语言有关系吗???待定!,编译型语言一般在编译的过程中就帮你分配好了,解释型要边解释边执行,所以为了防止出现数据不安全的情况加上了这个锁,这是所有解释型语言的弊端??)
但是有了这个锁我们就不能并发了吗?当我们的程序是偏计算的,也就是cpu占用率很高的程序(cpu一直在计算),就不行了,但是如果你的程序是I/O型的(一般你的程序都是这个)(input、访问网址网络延迟、打开/关闭文件读写),在什么情况下用的到高并发呢(金融计算会用到,人工智能(阿尔法狗),但是一般的业务场景用不到,爬网页,多用户网站、聊天软件、处理文件),I/O型的操作很少占用CPU,那么多线程还是可以并发的,因为cpu只是快速的调度线程,而线程里面并没有什么计算,就像一堆的网络请求,我cpu非常快速的一个一个的将你的多线程调度出去,你的线程就去执行I/O操作了,
详细介绍:链接:https://www.cnblogs.com/clschao/articles/9705317.html
二. 线程队列
class queue.
Queue
(maxsize=0) #先进先出
1 import queue #不需要通过threading模块里面导入,直接import queue就可以了,这是python自带的 2 #用法基本和我们进程multiprocess中的queue是一样的 3 q=queue.Queue() 4 q.put('first') 5 q.put('second') 6 q.put('third') 7 # q.put_nowait() #没有数据就报错,可以通过try来搞 8 print(q.get()) 9 print(q.get()) 10 print(q.get()) 11 # q.get_nowait() #没有数据就报错,可以通过try来搞 12 ''' 13 结果(先进先出): 14 first 15 second 16 third 17 '''
View Code
class queue.
LifoQueue
(maxsize=0) #last in fisrt out
import queueq=queue.LifoQueue() #队列,类似于栈,栈我们提过吗,是不是先进后出的顺序啊 q.put('first') q.put('second') q.put('third') # q.put_nowait()print(q.get()) print(q.get()) print(q.get()) # q.get_nowait() ''' 结果(后进先出): third second first '''
View Code
class queue.
PriorityQueue
(maxsize=0) #存储数据时可设置优先级的队列
1 import queue 2 3 q=queue.PriorityQueue() 4 #put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高 5 q.put((-10,'a')) 6 q.put((-5,'a')) #负数也可以 7 # q.put((20,'ws')) #如果两个值的优先级一样,那么按照后面的值的acsii码顺序来排序,如果字符串第一个数元素相同,比较第二个元素的acsii码顺序 8 # q.put((20,'wd')) 9 # q.put((20,{'a':11})) #TypeError: unorderable types: dict() < dict() 不能是字典 10 # q.put((20,('w',1))) #优先级相同的两个数据,他们后面的值必须是相同的数据类型才能比较,可以是元祖,也是通过元素的ascii码顺序来排序 11 12 q.put((20,'b')) 13 q.put((20,'a')) 14 q.put((0,'b')) 15 q.put((30,'c')) 16 17 print(q.get()) 18 print(q.get()) 19 print(q.get()) 20 print(q.get()) 21 print(q.get()) 22 print(q.get()) 23 ''' 24 结果(数字越小优先级越高,优先级高的优先出队): 25 '''
View Code
这三种队列都是线程安全的,不会出现多个线程抢占同一个资源或数据的情况。
三. 线程池--python标准模块 concurrent.futures
到这里就差我们的线程池没有讲了,我们用一个新的模块给大家讲,早期的时候我们没有线程池,现在python提供了一个新的标准或者说内置的模块,这个模块里面提供了新的线程池和进程池,之前我们说的进程池是在multiprocessing里面的,现在这个在这个新的模块里面,他俩用法上是一样的。
为什么要将进程池和线程池放到一起呢,是为了统一使用方式,使用threadPollExecutor和ProcessPollExecutor的方式一样,而且只要通过这个concurrent.futures导入就可以直接用他们两个了
1 concurrent.futures模块提供了高度封装的异步调用接口 2 ThreadPoolExecutor:线程池,提供异步调用 3 ProcessPoolExecutor: 进程池,提供异步调用 4 Both implement the same interface, which is defined by the abstract Executor class. 5 6 #2 基本方法 7 #submit(fn, *args, **kwargs) 8 异步提交任务 9 10 #map(func, *iterables, timeout=None, chunksize=1) 11 取代for循环submit的操作 12 13 #shutdown(wait=True) 14 相当于进程池的pool.close()+pool.join()操作 15 wait=True,等待池内所有任务执行完毕回收完资源后才继续 16 wait=False,立即返回,并不会等待池内的任务执行完毕 17 但不管wait参数为何值,整个程序都会等到所有任务执行完毕 18 submit和map必须在shutdown之前 19 20 #result(timeout=None) 21 取得结果 22 23 #add_done_callback(fn) 24 回调函
主要方法
1 import time 2 import os 3 import threading 4 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor 5 6 def func(n): 7 time.sleep(2) 8 print('%s打印的:'%(threading.get_ident()),n) 9 return n*n 10 tpool = ThreadPoolExecutor(max_workers=5) #默认一般起线程的数据不超过CPU个数*5 11 # tpool = ProcessPoolExecutor(max_workers=5) #进程池的使用只需要将上面的ThreadPoolExecutor改为ProcessPoolExecutor就行了,其他都不用改 12 #异步执行 13 t_lst = [] 14 for i in range(5): 15 t = tpool.submit(func,i) #提交执行函数,返回一个结果对象,i作为任务函数的参数 def submit(self, fn, *args, **kwargs): 可以传任意形式的参数 16 t_lst.append(t) # 17 # print(t.result()) 18 #这个返回的结果对象t,不能直接去拿结果,不然又变成串行了,可以理解为拿到一个号码,等所有线程的结果都出来之后,我们再去通过结果对象t获取结果 19 tpool.shutdown() #起到原来的close阻止新任务进来 + join的作用,等待所有的线程执行完毕 20 print('主线程') 21 for ti in t_lst: 22 print('>>>>',ti.result()) 23 24 # 我们还可以不用shutdown(),用下面这种方式 25 # while 1: 26 # for n,ti in enumerate(t_lst): 27 # print('>>>>', ti.result(),n) 28 # time.sleep(2) #每个两秒去去一次结果,哪个有结果了,就可以取出哪一个,想表达的意思就是说不用等到所有的结果都出来再去取,可以轮询着去取结果,因为你的任务需要执行的时间很长,那么你需要等很久才能拿到结果,通过这样的方式可以将快速出来的结果先拿出来。如果有的结果对象里面还没有执行结果,那么你什么也取不到,这一点要注意,不是空的,是什么也取不到,那怎么判断我已经取出了哪一个的结果,可以通过枚举enumerate来搞,记录你是哪一个位置的结果对象的结果已经被取过了,取过的就不再取了 29 30 #结果分析: 打印的结果是没有顺序的,因为到了func函数中的sleep的时候线程会切换,谁先打印就没准儿了,但是最后的我们通过结果对象取结果的时候拿到的是有序的,因为我们主线程进行for循环的时候,我们是按顺序将结果对象添加到列表中的。 31 # 37220打印的: 0 32 # 32292打印的: 4 33 # 33444打印的: 1 34 # 30068打印的: 2 35 # 29884打印的: 3 36 # 主线程 37 # >>>> 0 38 # >>>> 1 39 # >>>> 4 40 # >>>> 9 41 # >>>> 16
ThreadPoolExecutor
ProcessPoolExecutor的使用
只需要将这一行代码改为下面这一行就可以了,其他的代码都不用变 tpool = ThreadPoolExecutor(max_workers=5) #默认一般起线程的数据不超过CPU个数*5 # tpool = ProcessPoolExecutor(max_workers=5) 用法和线程是一样的
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import threading import os,time,random def task(n):print('%s is runing' %threading.get_ident())time.sleep(random.randint(1,3))return n**2if __name__ == '__main__':executor=ThreadPoolExecutor(max_workers=3)# for i in range(11):# future=executor.submit(task,i) s = executor.map(task,range(1,5)) #map取代了for+submitprint([i for i in s])
map方法
import time import os import threading from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutordef func(n):time.sleep(2)return n*ndef call_back(m):print('结果为:%s'%(m.result()))tpool = ThreadPoolExecutor(max_workers=5) t_lst = [] for i in range(5):t = tpool.submit(func,i).add_done_callback(call_back)
回调函数简单应用
转载于:https://www.cnblogs.com/Knight-huang/p/10058714.html
day34 GIL锁,线程队列,线程池相关推荐
- python多线程队列和池_Python3 从零单排28_线程队列进程池线程池
1.线程队列 线程队列有三种:先进先出,后进先出,按优先级进出,具体如下: 1 importqueue2 3 #先进先出 4 q = queue.Queue(3)5 6 q.put(1)7 q.put ...
- Python 线程----线程方法,线程事件,线程队列,线程池,GIL锁,协程,Greenlet
主要内容: 线程的一些其他方法 线程事件 线程队列 线程池 GIL锁 协程 Greenlet Gevent 一. 线程(threading)的一些其他方法 from threading import ...
- 线程队列 线程池 协程
1 . 线程队列 from multiprocessing Queue , JoinableQueue #进程IPC队列 from queue import Queue #线程队列 先进先出 f ...
- 线程队列,线程池和协程
线程的其他方法: threading.current_thread() #当前线程对象 getName() # 获取线程名 ident # 获取线程id threading.enumerate ...
- 0820Python总结-线程队列,进程池和线程池,回调函数,协程
一.线程队列 from queue import Queue put 存 get 取 put_nowait 存,超出了队列长度,报错 get_nowait 取,没数据时,直接报错 Linux Wind ...
- 线程队列,线程池,协程
线程 queue 线程的queue,类似于进程 作用也是类似,queue(n)规范放入值的数量 queue.Queue(maxsize = 0) 这个和之前一样是为了实现先进先出 import que ...
- 线程的创建 锁 Threading模块 事件 条件 定时器 队列 线程池 回调函数
线程的创建: 创建线程的方式1: from threading import Thread import time def sayhi(name):time.sleep(2)print('%s say ...
- 【python线程与GIL 锁】
文章目录 消息队列 IPC 机制(进程间通信) 生产者消费者模型 生产者与消费者是什么模型 模型的三要素 通过代码来实现一下生产者消费者模型 什么是线程 线程实现TCP服务端开发 线程join方法 线 ...
- GIL锁,线程锁(互斥锁)和递归锁
GIL锁(Global Interpreter Lock):CPython才会有(是这种解释器的历史遗留问题),Python程序执行前,先获得GIL锁,然后每执行100个指令,解释器就自动释放GIL锁 ...
- python结束线程池正在运行的线程_python之线程与线程池
#进程是资源分配的最小单位,线程是CPU调度的最小单位.每一个进程中至少有一个线程.#传统的不确切使用线程的程序称为只含有一个线程或单线程程序,而可以使用线程的程序被称为多线程程序,在程序中使用一个线 ...
最新文章
- Linux系统命令审计
- ISE与Modelsim联合仿真关联设置
- KYOCERA Programming Contest 2021(AtCoder Beginner Contest 200)题解
- 我们是怎样发出声音的?
- Android入门简书,android ndk开发入门随笔(一)
- 李彦宏纳猛将,技术才是百度的未来
- mysql o_Mysql数据类型
- 取名字_新生婴儿取名字大全2021
- 《Spring5官方文档》新功能(4,3)
- 2020-我的后端开发秋招之路
- (十三)中介者模式详解(玄幻版)
- 迅雷7核心技术Bolt界面引擎正式开放
- 虚拟机上用U盘安装系统
- 微信计数器|微信自动加好友|微信自动通过好友|微信HOOK|微信静默清粉
- 为什么程序猿996会猝死,而企业家007却不会?
- 解决esp8266无法连接手机和电脑热点的问题
- iOS10 开发适配问题 看到的整理
- 街道字符识别赛题理解
- prometheus的alertmanager开机启动报错
- django自带模块实现翻页功能