56星座屋
当前位置: 首页 星座百科

python编程算法与数据结构(程序员们看过来)

时间:2023-05-25 作者: 小编 阅读量: 4 栏目名: 星座百科

2GIL1简介全局解释器锁,进程级别的锁GILCpython在解释器进程中有一把锁,叫做GIL全局解释器锁。GIL保证Cpython进程中,当前时刻只有一个线程执行代码,甚至在多核情况下,也是如此。IO密集型,多线程解决,CPU密集型,多进程解决,绕开GIL。会降低Cpython单线程的执行效率。

51CTO学院会经常给大家分享各种技术文章,工程狮/程序猿们多多关注一 数据结构和GIL

1 queue

标准库queue模块,提供FIFO的queue、LIFO的队列,优先队列

Queue 类是线程安全的,适用于多线程间安全的交换数据,内部使用了Lock和Condition


为什么说容器的大小不准确,其原因是如果不加锁,是不可能获取到准确的大小的,因为你刚读取了一个大小,还没取走,有可能被就被其他线程修改了,queue类的size虽然加了锁,但是依然不能保证立即get,put就能成功,因为读取大小和get,put方法是分来的。

2 GIL

1 简介

全局解释器锁,进程级别的锁GIL

Cpython在解释器进程中有一把锁,叫做GIL全局解释器锁。

GIL 保证Cpython进程中,当前时刻只有一个线程执行代码,甚至在多核情况下,也是如此。

2 IO 密集型和CPU密集型

Cpython中

IO 密集型,由于线程阻塞,就会调度其他线程

CPU密集型,当前线程可能连续获取GIL,导致其他线程几乎无法使用CPU,若要唤醒其他线程,则需要准备数据,其代价是高昂的。


IO 密集型,多线程解决,CPU密集型,多进程解决,绕开GIL。

python中绝大多数内置数据结构的读写操作都是原子操作


由于GIL 的存在,python的内置数据类型在多线程编程的时候就变得安全了,但是实际上他们本身不是线程安全类型的

3 保留GIL 原因

Guido坚持的简单哲学,对于初学者门槛低,不需要高深的系统知识也能安全,简单的使用python。

而移除GIL。会降低Cpython单线程的执行效率。

4 验证其是否是单线程

相关实例

import loggingimport datetimelogging.basicConfig(level=logging.INFO,format="%(asctime)s %(threadName)s %(message)s ")start=datetime.datetime.now()def calc(): sum=0 for _ in range(1000000000): sum =1calc()calc()calc()calc()calc()delta=(datetime.datetime.now()-start).total_seconds()logging.info(delta)

多线程模式下的计算结果

import loggingimport datetimeimport threadinglogging.basicConfig(level=logging.INFO,format="%(asctime)s %(threadName)s %(message)s ")start=datetime.datetime.now()def calc(): sum=0 for _ in range(1000000000): sum =1lst=[]for _ in range(5): t=threading.Thread(target=calc) t.start() lst.append(t)for t in lst: t.join()delta=(datetime.datetime.now()-start).total_seconds()print (delta)

结果如下

从这两个程序来看,Cpython中多线程根本没有优势,和一个线程执行的时间相当,因为存在GIL

二 多进程

1 概念

1 多进程描述

由于python中的GIL ,多线程不是CPU密集型程序的最好选择

多进程可以在完全独立的进程中运行程序,可以充分利用多处理器

但是进程本身的隔离带来数据不共享也是一个问题,且线程比进程轻量的多

多进程也是解决并发的一种手段

2 进程和线程的异同

相同点:

进程是可以终止的,线程是不能通过命令终止的,线程的终止要么抛出异常,要么程序本身执行完成。

进程间同步提供了和线程同步一样的类,使用方式也是一样的,使用效果也是类似,不过,进程间同步的代价要高于线程,而且底层实现不同。

multiprocessing 还提供了共享内存,服务器进程来共享数据,还提供了queue队列,匹配管道用于进程间通信


不同点

通信方式不同

1 多进程就是启用多个解释器进程,进程间通信必须序列化,反序列化

2 数据的安全性问题

多进程最好是在main中执行

多线程已经将数据进行处理了,其不需要再次进行序列化了

多进程传递必须序列化和反序列化。

3 进程应用

远程调用,RPC,跨网络

2 参数介绍

multiprocessing中的process类

process 类遵循了Thread类的API,减少了学习难度

不同进程可以完全调度到不同的CPU上执行

IO 密集型最好使用多线程

CPU 密集型最好使用多进程

进程提供的相关属性

名称 含义 pid 进程ID exitcode 进程退出的状态码 terminate() 终止指定进程 3 实例

import loggingimport datetimeimport multiprocessinglogging.basicConfig(level=logging.INFO,format="%(asctime)s %(threadName)s %(message)s ")start=datetime.datetime.now()def calc(i): sum=0 for _ in range(1000000000): sum =1lst=[]for i in range(5): p=multiprocessing.Process(target=calc,args=(i,),name="P-{}".format(i)) p.start() lst.append(p)for p in lst: p.join()delta=(datetime.datetime.now()-start).total_seconds()print (delta)

结果如下

多进程本身避开了进程和进程之间调度需要的时间,多核心都使用了,此处存在CPU的调度问题

多进程对CPU的提升是显而易见的。

单线程,多线程都跑了很长时间,而多进程只是用了1分半,是真正的并行

4 进程池相关

import loggingimport datetimeimport multiprocessinglogging.basicConfig(level=logging.INFO,format="%(asctime)s %(threadName)s %(message)s ")start=datetime.datetime.now()def calc(i): sum=0 for _ in range(1000000000): sum =1 print (i,sum)if __name__=='__main__': start=datetime.datetime.now() p=multiprocessing.Pool(5) # 此处用于初始化进程池,其池中的资源是可以复用的 for i in range(5): p.apply_async(calc,args=(i,)) p.close() # 下面要执行join,上面必须先close p.join() delta=(datetime.datetime.now()-start).total_seconds() print (delta)

结果如下

进程创建的多,使用进程池进行处理还是一种比较好的处理方式

5 多进程和多线程的选择

1 选择

1 CPU 密集型

Cpython 中使用了GIL,多线程的时候互相竞争,且多核优势不能发挥,python使用多进程效率更高

2 IO密集型

适合使用多线程,减少IO序列化开销,且在IO等待时,切换到其他线程继续执行,效率不错,当然多进程也适用于IO密集型

2 应用

请求/应答模型: WEB应用中常见的处理模型

master启动多个worker工作进程,一般和CPU数目相同

worker工作进程中启动多个线程,提高并发处理能力,worker处理用户的请求,往往需要等待数据

这就是nginx的工作模式

工作进程一般都和CPU核数相同,CPU的亲原性,进程在CPU的迁移成本比较高。

三 concurrent包

1 概念

concurrent.Futures

3.2 版本引入的模块

异步并行任务编程模块,提供一个高级的异步可执行的便利接口

提供了2个池执行器

ThreadPoolExecutor 异步调用的线程池的Executor

ProcessPoolExecutor 异步调用进程池的Executor

2 参数详解

方法 含义 ThreadPoolExecutor(max_workers=1) 池中至多创建max_workers个线程的池来同时异步执行,返回Executor实例 submit(fn,*args,**kwagrs) 提交执行的函数及参数,返回Future实例 shutdown(wait=True) 清理池 Future 类

方法 含义 result() 可以查看调用的返回结果 done() 如果调用被成功的取消或者执行完成,则返回为True cancelled() 如果调用被成功取消,返回True running() 如果正在运行且不能被取消,则返回True cancel() 尝试取消调用,如果已经执行且不能取消则返回False,否则返回True result(timeout=None) 取返回的结果,超时时为None,一直等待返回,超时设置到期,抛出concurrent.futures.TimeoutError异常 execption(timeout=None) 取返回的异常,超时为None,一直等待返回,超时设置到期,抛出concurrent.futures.TimeoutError异常 3 线程池相关实例

import loggingimport threadingfrom concurrent import futuresimport loggingimport timelogging.basicConfig(level=logging.INFO,format="%(asctime)-15s\t [%(processName)s:%(threadName)s,%(process)d:%(thread)8d] %(message)s")def worker(n): # 定义未来执行的任务 logging.info("begin to work{}".format(n)) time.sleep(5) logging.info("finished{}".format(n))# 创建一个线程池,池容量为3executor=futures.ThreadPoolExecutor(max_workers=3)fs=[]for i in range(3): f=executor.submit(worker,i) # 传入参数,返回Future对象 fs.append(f)for i in range(3,6): f=executor.submit(worker,i) # 传入参数,返回Future对象 fs.append(f)while True: time.sleep(2) logging.info(threading.enumerate()) #返回存活线程列表 flag=True for f in fs: logging.info(f.done()) # 如果被成功调用或取消完成,此处返回为True flag=flag and f.done() # 若都调用成功,则返回为True,否则则返回为False if flag: executor.shutdown() # 如果全部调用成功,则需要清理池 logging.info(threading.enumerate()) break

结果如下

其线程池中的线程是持续使用的,一旦创建好的线程,其不会变化,唯一不好的就是线程名未发生变化,但其最多影响了打印效果

4 进程池相关实例

import loggingimport threadingfrom concurrent import futuresimport loggingimport timelogging.basicConfig(level=logging.INFO,format="%(asctime)-15s\t [%(processName)s:%(threadName)s,%(process)d:%(thread)8d] %(message)s")def worker(n): # 定义未来执行的任务 logging.info("begin to work{}".format(n)) time.sleep(5) logging.info("finished{}".format(n))# 创建一个进程池,池容量为3executor=futures.ProcessPoolExecutor(max_workers=3)fs=[]for i in range(3): f=executor.submit(worker,i) # 传入参数,返回Future对象 fs.append(f)for i in range(3,6): f=executor.submit(worker,i) # 传入参数,返回Future对象 fs.append(f)while True: time.sleep(2) flag=True for f in fs: logging.info(f.done()) # 如果被成功调用或取消完成,此处返回为True flag=flag and f.done() # 若都调用成功,则返回为True,否则则返回为False if flag: executor.shutdown() # 如果全部调用成功,则需要清理池 break

结果如下

5 支持上下文管理

concurrent.futures.ProcessPoolExecutor 继承自concurrent.futures.base.Executor,而父类有enter,_exit方法,其是支持上下文管理的,可以使用with语句

import loggingimport threadingfrom concurrent import futuresimport loggingimport timelogging.basicConfig(level=logging.INFO,format="%(asctime)-15s\t [%(processName)s:%(threadName)s,%(process)d:%(thread)8d] %(message)s")def worker(n): # 定义未来执行的任务 logging.info("begin to work{}".format(n)) time.sleep(5) logging.info("finished{}".format(n))fs=[]with futures.ProcessPoolExecutor(max_workers=3) as executor: for i in range(6): futures=executor.submit(worker,i) fs.append(futures)while True: time.sleep(2) flag=True for f in fs: logging.info(f.done()) # 如果被成功调用或取消完成,此处返回为True flag=flag and f.done() # 若都调用成功,则返回为True,否则则返回为False if flag: executor.shutdown() # 如果全部调用成功,则需要清理池 break

结果如下

6 总结

统一了线程池,进程池的调用,简化了编程,是python简单的思想哲学的提现

唯一缺点: 无法设置线程名称

©著作权归作者所有:来自51CTO博客作者长跑者1号的原创作品,如需转载,请注明出处,否则将追究法律责任

,
    推荐阅读
  • 手抓饭的做法(怎么制作手抓饭)

    以下内容希望对你有帮助!手抓饭的做法食材:羊排、胡萝卜、洋葱、大米、山楂、葡萄干、花椒、孜然粉、食用油。大米用水泡半个小时,羊肉切小块,胡萝卜切丁或丝,洋葱切丁。锅里放油,放入洋葱炒出香味,再放入羊肉翻炒。倒水没过羊肉煮大约十分钟。把所有炒好的菜,肉,汤和葡萄干,全部倒入电饭锅,把泡好的米饭均匀撒在上面一层,1:2比例为好,开始焖20分钟。打开后,菜,肉,米饭拌匀即可食用。

  • jdk并发包使用教程(线程并发协调神器CountDownLatch和CyclicBarrier)

    就拿CountDownLatch来说,它的命名形象的表示了其能力属性,Count代表着计数,Down代表着计数器的递减操作,而Latch表示计数器递减后的结果动作。当计数器置为0后,阻塞的线程被释放。使用场景就像上文描述的,CountDownLatch就像是田径赛场上裁判员发射的发令枪,所有参赛的选手准备就绪后,发令枪一响,所有运动员闻声而动。这就好比公司组织团建,约定好了早上8点半在公司大门集合,那么司机师傅肯定要等到所有参加团建的同时都到齐后才会发车。

  • window10玩dnf卡顿(要怎么解决呢)

    接下来我们就一起去了解一下吧!window10玩dnf卡顿首先使用鼠标在桌面空白右击,并在弹出菜单中使用鼠标左击项目。在点击左侧的设项目,之后使用鼠标左击下方的项目,在弹出的菜单中使用鼠标左击项目。接着在设置列表中找到,使用鼠标左击窗口右侧的项目,在弹出的菜单中使用鼠标左击项目。设置完成后点击应用,保存设置即可。

  • 排球小组赛积分规则(排球赛小组积分制是怎么积分得出结果的)

    我们一起去了解并探讨一下这个问题吧!排球小组赛积分规则小组赛都是按照胜场计算,胜者加分。前4局比赛采用25分制,每个队只有赢得至少25分,并同时超过对方2分时,才胜1局。正式比赛采用5局3胜制,决胜局的比赛采用15分制,一队先得8分后,两队交换场区,按原位置顺序继续比赛到结束。在决胜局(第五局)的比赛,先获15分并领先对队2分为胜。

  • 敷毛孔收敛水的正确步骤(怎么敷毛孔收敛水)

    所以,第一步就是要清洗双手,避免脏污蔓延到脸上的毛孔里。卸掉面部彩妆,洗面奶清洗脸部,或用一些洗脸仪.洗完脸之后,姑娘们就可以开始用清洁面膜或者去黑头的护肤品了,更加安全。如果姑娘们急着出门,那么用完城收敛水之后做好保湿,使用质地清爽的面霜也是没问题的。无论何时,为了我们肌肤的年轻状态,使用保湿型面霜和乳液都是不可缺少的哦。

  • 沉香如屑杨紫造型不如女二(沉香如屑的3大争议)

    《沉香如屑》空降播出,网友吐槽剧情俗套,杨紫颜值不如孟子义——引言。很多网友喜欢杨紫,主要还是看实力,奈何《沉香如屑》里面,由于她与孟子义的同框画面有所差距,使得杨紫被无数网友吐槽脸垮了。随后,孟子义艳压杨紫的词条便登上了热搜。不仅女演员被拿来对比,成毅与张睿也同样被网友吐槽。因此,《沉香如屑》的前期预热有多火,如今其不如期待值的吐槽声就有多激烈。

  • 喜出望外望的意思(喜出望外解释)

    下面希望有你要的答案,我们一起来看看吧!喜出望外望的意思喜出望外的望释义:希望,意料。喜出望外,汉语成语,拼音是xǐchūwàngwài,释义:是指因遇到意外的喜事,心中非常高兴。出自宋·苏轼《与李之仪》。成语辨析近义词:大喜过望、喜从天降、欣喜若狂、眉开眼笑、如获至宝。

  • 超过30岁的女人必备护肤品(这3种功能性护肤品要常用)

    而且上了年纪之后,眼睛周围的皮肤也需要补水,这样才能做到提亮眼周皮肤,预防新的皱纹产生。随着年龄的增长,皮肤毛孔会变大,里面就会积攒更多的粉尘和垃圾,如果不及时的清理,就会引发闭口,肤色暗沉,角质层增厚等问题。不过清洁面膜也不宜多做,一周1次就够了,频繁的使用清洁面膜很容易破坏皮肤屏障,让皮肤变成敏感肌。另外在使用完清洁面膜之后一定要做好皮肤的补水工作,因为清洁面膜在清洁的同时还会带走一定的水分。

  • 家具定制安装,常见问题解决方法 定制家具出现问题如何处理

    定制家具安装往往存在各种问题,因此解决方案是主人需要理解和学习,勤学知装修网www.qinxuezhi.com帮助主人了解常见问题的解决方案。电路板没有边缘密封,因此这需要端子设计来考虑间隙。该板应平整,固定,并用钻头正确钻孔,钻头必须垂直!滑动门的一般问题相对较小,相对来说,一扇门被推到右边。右边有一个空隙,这是一个推拉门或门洞本身不垂直。

  • 春节幸福的祝福语(春节来临之际的祝福)

    春节幸福的祝福语事业龙飞凤舞,家庭龙凤呈祥,儿孙龙驹凤雏,健康龙马精神,思维龙腾虎跃,新的一年里愿朋友龙兴风聚,大有作为,万事龙章凤彩,福寿安康!春节人多伤脑筋,满城内外都拥挤,鞭炮烟花要远离,老人小孩少出行,睡眠规律人有劲,春节怎能不开心!春节祝福短信来到,祝你在新年里:事业如日中天,心情阳光灿烂,工资地覆天翻,未来风光无限,爱情浪漫依然,愉快游戏人间。