4月27日 python学习总结 GIL、进程池、线程池、同步、异步、阻塞、非阻塞

一、GIL:全局解释器锁  

  1 、GIL:全局解释器锁

GIL本质就是一把互斥锁,是夹在解释器身上的,

同一个进程内的所有线程都需要先抢到GIL锁,才能执行解释器代码

2、GIL的优缺点:

优点:  保证Cpython解释器内存管理的线程安全

缺点:同一进程内所有的线程同一时刻只能有一个执行,也就说Cpython解释器的多线程无法实现并行

二、GIL与多线程  

有了GIL的存在,同一时刻同一进程中只有一个线程被执行

听到这里,有的同学立马质问:进程可以利用多核,但是开销大,而python的多线程开销小,但却无法利用多核优势,也就是说python没用了,php才是最牛逼的语言?

要解决这个问题,我们需要在几个点上达成一致:

#1. cpu到底是用来做计算的,还是用来做I/O的?

#2. 多cpu,意味着可以有多个核并行完成计算,所以多核提升的是计算性能

#3. 每个cpu一旦遇到I/O阻塞,仍然需要等待,所以多核对I/O操作没什么用处 

一个工人相当于cpu,此时计算相当于工人在干活,I/O阻塞相当于为工人干活提供所需原材料的过程,工人干活的过程中如果没有原材料了,则工人干活的过程需要停止,直到等待原材料的到来。

如果你的工厂干的大多数任务都要有准备原材料的过程(I/O密集型),那么你有再多的工人,意义也不大,还不如一个人,在等材料的过程中让工人去干别的活,

反过来讲,如果你的工厂原材料都齐全,那当然是工人越多,效率越高

结论:

  对计算来说,cpu越多越好,但是对于I/O来说,再多的cpu也没用

  当然对运行一个程序来说,随着cpu的增多执行效率肯定会有所提高(不管提高幅度多大,总会有所提高),这是因为一个程序基本上不会是纯计算或者纯I/O,所以我们只能相对的去看一个程序到底是计算密集型还是I/O密集型,从而进一步分析python的多线程到底有无用武之地

#分析:
我们有四个任务需要处理,处理方式肯定是要玩出并发的效果,解决方案可以是:
方案一:开启四个进程
方案二:一个进程下,开启四个线程

#单核情况下,分析结果:
  如果四个任务是计算密集型,没有多核来并行计算,方案一徒增了创建进程的开销,方案二胜
  如果四个任务是I/O密集型,方案一创建进程的开销大,且进程的切换速度远不如线程,方案二胜

#多核情况下,分析结果:
  如果四个任务是计算密集型,多核意味着并行计算,在python中一个进程中同一时刻只有一个线程执行用不上多核,方案一胜
  如果四个任务是I/O密集型,再多的核也解决不了I/O问题,方案二胜

#结论:现在的计算机基本上都是多核,python对于计算密集型的任务开多线程的效率并不能带来多大性能上的提升,甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。

  

多线程性能测试

 

 1 from multiprocessing import Process
 2 from threading import Thread
 3 import os,time
 4 def work():
 5     res=0
 6     for i in range(100000000):
 7         res*=i
 8
 9
10 if __name__ == ‘__main__‘:
11     l=[]
12     print(os.cpu_count()) #本机为4核
13     start=time.time()
14     for i in range(4):
15         p=Process(target=work) #耗时5s多
16         p=Thread(target=work) #耗时18s多
17         l.append(p)
18         p.start()
19     for p in l:
20         p.join()
21     stop=time.time()
22     print(‘run time is %s‘ %(stop-start))

计算密集型:多进程效率高

 1 from multiprocessing import Process
 2 from threading import Thread
 3 import threading
 4 import os,time
 5 def work():
 6     time.sleep(2)
 7     print(‘===>‘)
 8
 9 if __name__ == ‘__main__‘:
10     l=[]
11     print(os.cpu_count()) #本机为4核
12     start=time.time()
13     for i in range(400):
14         # p=Process(target=work) #耗时12s多,大部分时间耗费在创建进程上
15         p=Thread(target=work) #耗时2s多
16         l.append(p)
17         p.start()
18     for p in l:
19         p.join()
20     stop=time.time()
21     print(‘run time is %s‘ %(stop-start))

I/O密集型:多线程效率高

三、进程池与线程池    

进程池vs线程池

为什么要用“池”:  池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务

池子内什么时候装进程:并发的任务属于计算密集型

池子内什么时候装线程:并发的任务属于IO密集型

进程池  

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time,os,random

def task(x):
    print(‘%s 接客‘ %os.getpid())
    time.sleep(random.randint(2,5))
    return x**2

if __name__ == ‘__main__‘:
    p=ProcessPoolExecutor() # 默认开启的进程数是cpu的核数

    # alex,武佩奇,杨里,吴晨芋,张三

    for i in range(20):
        p.submit(task,i)

线程池

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time,os,random

def task(x):
    print(‘%s 接客‘ %x)
    time.sleep(random.randint(2,5))
    return x**2

if __name__ == ‘__main__‘:
    p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5

    # alex,武佩奇,杨里,吴晨芋,张三

    for i in range(20):
        p.submit(task,i)

四、同步、异步、阻塞、非阻塞

1、阻塞与非阻塞指的是程序的两种运行状态

阻塞:遇到IO就发生阻塞,程序一旦遇到阻塞操作就会停在原地,并且立刻释放CPU资源

非阻塞(就绪态或运行态):没有遇到IO操作,或者通过某种手段让程序即便是遇到IO操作也不会停在原地,执行其他操作,力求尽可能多的占有CPU

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

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

异步调用:提交完任务后,不在原地等待,直接执行下一行代码,结果?

异步调用

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time,os,random

def task(x):
    print(‘%s 接客‘ %x)
    time.sleep(random.randint(1,3))
    return x**2

if __name__ == ‘__main__‘:
    # 异步调用
    p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5

    # alex,武佩奇,杨里,吴晨芋,张三

    obj_l=[]
    for i in range(10):
        obj=p.submit(task,i)
        obj_l.append(obj)

    # p.close()
    # p.join()
    p.shutdown(wait=True)

    print(obj_l[3].result())
    print(‘主‘)

  

 同步调用

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time,os,random

def task(x):
    print(‘%s 接客‘ %x)
    time.sleep(random.randint(1,3))
    return x**2

if __name__ == ‘__main__‘:

    # 同步调用
    p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5

    # alex,武佩奇,杨里,吴晨芋,张三

    for i in range(10):
        res=p.submit(task,i).result()

    print(‘主‘)

原文地址:https://www.cnblogs.com/95lyj/p/8963001.html

时间: 2024-11-18 10:11:05

4月27日 python学习总结 GIL、进程池、线程池、同步、异步、阻塞、非阻塞的相关文章

4月27日下午学习日志

4月27日下午看了通信工程的书和视频,了解了内核功能 内线功能:进程管理,内存管理,文件系统,网络功能,硬件驱动,安全机制 其中进程是正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,比如说CPU(寄存器),IO,内存,网络资源等: 内存管理,是指软件运行时对计算机内存资源的分配和使用的技术: 文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于NAND Flash的固态硬盘)或分区上的文件的方法和数据结构:

4月27日上午学习日志

2017年4月27日上午把昨天记忆的英语单词的多种词意用自己组句的方法联系起来再记忆一遍,然后再学10个考研高频词汇,完成英语app的打卡.

4月12日 python学习总结 继承和派生

一.继承 什么是继承:   继承是一种新建类的方式,在python中支持一个子类继承多个父类   新建类称为子类或派生类   父类可以称之为基类或者超类   子类会遗传父类的属性 2.  为什么继承 减少代码冗余 3. 定义方式: class Parent: pass class SubClass(Parent): pass print(SubClass.__bases__) #查看类的父类 4.  继承,调用父类方法以及self class Foo: def f1(self): print('

Python学习笔记 - day13 - 进程与线程

概述 我们都知道windows是支持多任务的操作系统. 什么叫"多任务"呢?简单地说,就是操作系统可以同时运行多个任务.打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行.还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已. 现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务.由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢? 答案就是操作系统轮流让各个任务交替执行,任

6月11日 python学习总结 框架理论

Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 半成品自定义web框架 import socket sk = socket.socket() sk.bind(("127.0.0.1", 80)) sk.listen() while True: conn, addr = sk.accept() data = conn.recv(8096) conn.send(b"

2016年4月27日_JAVA学习笔记_JAVA中常见的API(一)String

1.String在JAVA中是一个单独的类,只不过是一种特殊的,专门用来表示字符串的类.之前接触到的创建方式很简单,就是跟C语言中创建变量一样, String aString = "This is a String."; //变量类型为String,变量名为aString,内容为"This is a String.". 在学习API时,接触到了一种特别的创建方式.因为String是一个类,那么就肯定可以用其构造器方法来创建相应的对象. String aString

2016年3月27日_JAVA学习笔记

1.前一天晚上开通了博客,今天是第一天写博客,或者说是日记吧. 右手的骨折还没完全康复,也还不知道要多久.现在仍然是左手用鼠标,而且无名指明显也不会打字了.毕竟已经3个月没有怎么用过了,所以完全恢复应该还需要一段时间吧. 2.JAVA中存在有嵌套类的情况,所谓嵌套类就是在一个类中还存在着一个类,这个类以成员变量的形式存在.在毕老师的视频中介绍了几种内部类的调用方法. //外部.内部.方法为非静态, //外部.内部 n = new 外部().new 内部(); //n.show(); //外部和方

2015年5月27日 Unity学习疑问记录之新GUI

学习Unity 4.6新GUI系统 http://segmentfault.com/a/1190000000642686

4月4日 python学习总结

---恢复内容开始--- 1.序列化和反序列化 我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling. 反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling. 为什么要保持序列化?   1.持久化状态 2.跨平台数据交互 各种语言之间,实现数据相互转换 2.json.eval.pickle eval()虽然也能进行数据提取,但是,eval()只能识别python 定义的数据类型,用来做序列化不具有跨平台型 Json的使用