18-进程&协程
进程
-
概念
- 进程是程序的一次执行过程, 正在进行的一个过程或者说一个任务,而负责执行任务的则是CPU.
-
进程的生命周期
- 当操作系统要完成某个任务时,它会创建一个进程。当进程完成任务之后,系统就会撤销这个进程,收回它所占用的资源。从创建到撤销的时间段就是进程的生命周期
-
并行与并发
- 并行
- 多个任务同时运行,只有具备多个cpu才能实现并行,含有几个cpu,也就意味着在同一时刻可以执行几个任务。
- 并发
- 是伪并行,即看起来是同时运行的,实际上是单个CPU在多道程序之间来回的进行切换。
- 并行
-
同步与异步
- 同步
- 指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去。
- 异步
- 指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进行处理,这样可以提高执行的效率。
- 同步
-
创建进程
-
import multiprocessing # 方法1 import os def process1(*args): print('子进程:', args) def create_process1(): p = multiprocessing.Process(target=process1, args=('故宫', '长城')) p.start() # p.join() # 方法2 class MyProcess(multiprocessing.Process): def __init__(self): super().__init__() def run(self): print('子进程:', multiprocessing.current_process().name) print('进程id:', self.pid) def create_process2(): p = MyProcess() p.start() print('p进程', p.pid) print('主进程', os.getpid()) # 进程id if __name__ == '__main__': # create_process1() create_process2()
-
-
进程锁
-
import multiprocessing import time # multiprocessing.Lock,将锁作为变量传入 lock = multiprocessing.Lock() def f(i, lock): with lock: print(f'第{i}个进程加锁') time.sleep(3) print(f'第{i}个进程解锁') if __name__ == '__main__': # 进程锁:了解 for i in range(5): p = multiprocessing.Process(target=f, args=(i, lock)) # 不同的进程间内存独立 p.start()
-
-
信号量
-
import multiprocessing import time # 控制进程的最大并发数 # 将信号量作为变量传入,因为进程之间内存是相互独立的 def f2(i, sem): with sem: print(f'子进程{i}开始执行') time.sleep(3) print(f'子进程{i}结束') if __name__ == '__main__': # 信号量:控制最大的进程并发数 sem = multiprocessing.Semaphore(3) for i in range(20): multiprocessing.Process(target=f2, args=(i, sem)).start()
-
-
异步变同步
- .join()
协程
-
概念
-
# 首先我们得知道协程是啥?协程其实可以认为是比线程更小的执行单元。为啥说他是一个执行单元,因为他自带CPU上下文。这样只要在合适的时机,我们可以把一个协程切换到另一个协程,只要这个过程中保存或恢复CPU上下文那么程序还是可以运行的。 # 通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。
-
协程,又称微线程,纤程。英文名Coroutine。
-
-
协程与线程的区别
-
1.最大的优势就是协程极高的执行效率。
-
# 因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
-
-
2.第二大优势就是不需要多线程的锁机制
-
# 因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
-
-
-
利用多核CPU
- 多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
-
缺点
- 它不能同时将CPU的多个核用上,只能使用一个核
-
创建协程
-
greenlet + switch
-
# greenlet + switch # 通过time.sleep()自己控制 from greenlet import greenlet import time def fn1(): print('协程1') time.sleep(3) g2.switch() # 切换到g2协程 print('英国是个小地方') time.sleep(3) g2.switch() def fn2(): print('协程2') time.sleep(3) g1.switch() print('李嘉诚买下了半个英国') time.sleep(3) if __name__ == '__main__': g1 = greenlet(fn1) g2 = greenlet(fn2) g1.switch() # 切换到g1协程 ''' 协程1 协程2 英国是个小地方 李嘉诚买下了半个英国 '''
-
-
gevent + sleep
-
import gevent def fn1(): gevent.sleep(1) print('协程1') gevent.sleep(4) print('窗前明月光') def fn2(): gevent.sleep(2) print('协程2') gevent.sleep(2) print('疑是地上霜') if __name__ == '__main__': g1 = gevent.spawn(fn1) # 传参,g1 = gevent.spawn(fn1, 1) g2 = gevent.spawn(fn2) gevent.joinall([g1, g2]) # g1.join() ''' 协程1 协程2 疑是地上霜 窗前明月光 '''
-
-
gevent + monkey
-
import gevent import requests import time def fn(url): print('协程:', url) response = requests.get(url) print(url, len(response.text)) if __name__ == '__main__': url_list = [ 'http://www.baidu.com', 'http://www.qq.com', 'http://www.ifeng.com', ] # 创建协程 g_list = [] for url in url_list: g = gevent.spawn(fn, url) # g.join() g_list.append(g) # 执行所有的协程 gevent.joinall(g_list) ''' 发生阻塞,并没有达到多协程的效果(多个任务来回切换)。 协程: http://www.baidu.com http://www.baidu.com 2381 协程: http://www.qq.com http://www.qq.com 98603 协程: http://www.ifeng.com http://www.ifeng.com 223424 '''
-
import gevent from gevent import monkey # 导入猴子补丁 monkey.patch_all() # 自动切换协程 # 导入包的时候,以上三句一定放在最上面 import requests import time def fn(url): print('协程:', url) response = requests.get(url) print(url, len(response.text)) if __name__ == '__main__': url_list = [ 'http://www.baidu.com', 'http://www.qq.com', 'http://www.ifeng.com', ] # 创建协程 g_list = [] for url in url_list: g = gevent.spawn(fn, url) # g.join() g_list.append(g) # 执行所有的协程 gevent.joinall(g_list) ''' 猴子补丁:在运行时修改类或模块,而不改动源码。 协程: http://www.baidu.com 协程: http://www.qq.com 协程: http://www.ifeng.com http://www.baidu.com 2381 http://www.qq.com 98620 http://www.ifeng.com 223428 '''
-
-
高阶函数
-
向函数中传入函数
-
sorted()
-
reversed()
-
map()
-
映射
-
运算后是map类型,要强转
-
# map():可以对某列表中的每个元素进行统一的处理,得到新的列表 list1 = [1, 2, 3, 4] # list2 = map(lambda x:x**2, list1) # list2 = [i**2 for i in list1] list2 = map(str, list1) # ['1', '2', '3', '4'] print(list2) # <map object at 0x000001C641FA2308> print(list(list2)) # ['1', '2', '3', '4'] list3 = [1, 2, 3, 4] list4 = [1, 2, 3, 4] list5 = map(lambda x, y: x+y, list3, list4) print(list(list5)) # [2, 4, 6, 8]
-
-
reduce()
-
累计运算
-
# reduce():累计运算 from functools import reduce n = reduce(lambda x, y: x+y, range(1, 101)) print(n) #5050
-
-
-
filter()
-
过滤
-
运算后是filter类型,要强转
-
# filter():过滤 list6 = [1, 2, 3, 4, 5, 6, 7, 8, 9] list7 = filter(lambda x: x % 3 == 0, list6) print(list7) # <filter object at 0x000001A9C6F61908> print(list(list7)) # [3, 6, 9]
-
-
-
json
-
import json # json字符串 => json对象 :json解析/json反序列化 json_str = '{"name": "鹿晗", "age": 30}' json_obj = json.loads(json_str) print(json_obj) # {'name': '鹿晗', 'age': 30} print(type(json_obj)) # <class 'dict'> # json对象 => json字符串 :json序列化(使用更多) json_obj = {"name": "鹿晗", "age": 30} json_str = json.dumps(json_obj) print(json_str) # '{"name": "\u9e7f\u6657", "age": 30}' print(type(json_str)) # <class 'str'>
-
json是一种数据格式
-
json的两种存在形式
- json字符串
- json对象
- json解析/json反序列化
- json字符串 => json对象
- json.loads()
- json序列化
- json对象 => json字符串
- json.dumps()
pickle模块
-
作用
- 1.将Python对象(列表,字典,元组)存入文件中,做一个持久化存储
- 2.将存入文件的内容取出
-
存入
- dump()
-
取出
- load()
-
import pickle # 存入文件 stars = ['鹿晗', '肖战', '蔡徐坤', '王一博', '吴亦凡'] fp = open('stars.txt', 'wb') pickle.dump(stars, fp) fp.close() # 从文件中取出 fp2 = open('stars.txt', 'rb') res = pickle.load(fp2) print(res) # ['鹿晗', '肖战', '蔡徐坤', '王一博', '吴亦凡'] print(type(res)) # <class 'list'>