统一时间有多个任务执行
Python默认单任务
进程:是资源分配基本单位
线程:cpu调度基本单位
协程:单线程执行多任务
切换效率:协程>线程>进程
高效方式:进程+协程
选择问题:
多进程:
密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。
缺陷:多个进程之间通信成本高,切换开销大。多线程:
密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。
缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。协程:
当程序中存在大量不需要CPU的操作时(IO),适用于协程;
一个程序至少有一个进程,一个进程至少有一个线程
线程并发度高
线程共享内存,提高运行效率
线程不能独立运行,必须存在进程中
大量重复工作,用进程
cpython解释器
主线程:程序启动,创建,其它线程结束,关闭
子线程:主线程可以创建子线程
子线程创建步骤
1.导入threading
2.obj_thread = threading.thread(obj)
3.obj_thread.start()
获取线程数量(活跃):
thread_num = len(threading.enumerate())
获取线程对象中有名称
threading.current_thread()
参数顺序一致
传参数有3种方法
1、元组传递
threading.Thread(target=sing, args=(10,100,1000))
2、字典传递
thread_sing = threading.Thread(target=sing, kwargs={“a”: 10, “c”: 1000, “b”: 100})
3、混合传递
thread_sing = threading.Thread(target=sing, args=(10, ), kwargs={“c”: 1000, “b”: 10})
线程的执行顺序是无序的
表示子线程就守护了主线程(主线程结束后,子线程也结束)
thread_work.setDaemon(True)
并发:任务数大于cpu核数
并行:任务数小于cpu核数
自定义线程类
1、继承threading
2、重写run
3、通过子类对象.start()
共享全局变量问题
资源竞争问题
t1.join() t1先执行
同步:多任务中,有先后顺序
异步:多任务中,没有先后顺序,多个任务同时执行
保证统一时间只有一个任务执行
创建锁
lock1 = threading.Lock()
上锁
lock1.acquire()
解锁
lock1.release()
进程:资源分配的基本单位,也是线程的容器
1、导入模块
import multiprocessing
2、通过模块提供的Process类创建进程对象
process_obj = multiprocessing.Process(target=work1)
3、启动进程
process_obj.start()
设置进程名称
multiprocessing.Process(target=work1, name=“P1”)
获取进程名称
multiprocessing.current_process()
获取进程编号
multiprocessing.current_process().pid
使用os模块来获取:os.getpid()
Kill -9 进程编号 杀进程
1)使用 args 传递元组
2)使用 kwargs 传递字典
3)混合使用 args 和 kwargs
#设置线程的setDaemon(True)
#设置 process_obj 子进程守护主进程
#process_obj.daemon = True
#terminate() 终止子进程的执行
process_obj.terminate()
#1)创建队列(指定长度)
#multiprocessing.Queue(n) n 表示队列长度
queue = multiprocessing.Queue(5)
#2)放值
#queue.put(值) 向队列中放入值
queue.put(1) # 放入1数字
queue.put(“hello”)
queue.put([1,2,3])
queue.put((4,5,6))
queue.put({“a”: 10, “b”: 100})
#长度为5,放入第6个数据后,队列就进行入了阻塞状态,默认会等待队列先取出值再放入新的值
#queue.put(10)
#put_nowait() 表示放入值,如果已满,不再等待,直接报错
#queue.put_nowait(10)
##3)取值
#value = queue.get()
#print(value)
#print("–"*20)
##get_nowait() 当队列已空的时候,不会等待放入新的值,直接报错
#value = queue.get_nowait()
#print(value)
#print("–"*20)
#1、判断是否已满
#queue.full() 判断队列是否已满, True 满 False 未满
isFull = queue.full()
#3、取出队列中消息的个数
print(“消息的个数:”, queue.qsize()) #
#2、判断是否已经为空 True 空 False 未空
isEmpty = queue.empty()
print(“isEmpty —>”, isEmpty)
"""
思路:
1、准备两个进程
2、准备一个队列 一个进程向队列中写入数据,然后吧队列传递到另外一个进程
3、另外一个进程读取数据
"""
import time
import multiprocessing
# 1、写入数据到队列的函数
def write_queue(queue):
# for循环,向队列写入数据
for i in range(10):
# 判断队列是否已满
if queue.full():
print("队列已满!")
break
# 向队列中放入值
queue.put(i)
print("写入成功,已经写入:",i)
time.sleep(0.5)
# 2、读取队列数据并显示的函数
def read_queue(queue):
while True:
# 判断队列是否已经为空
if queue.qsize() == 0:
print("队列已空!")
break
# 从队列中读取数据
value = queue.get()
print("已经读取:", value)
if __name__ == '__main__':
# 3、创建一个空的队列
queue = multiprocessing.Queue(5)
# 4、创建2个进程,分别写数据、读数据
write_queue1 = multiprocessing.Process(target=write_queue, args=(queue, ))
read_queue1 = multiprocessing.Process(target=read_queue, args=(queue, ))
write_queue1.start()
# 优先让写数据进程执行结束后,再读取数据
# write_queue1.join() 先让写的进程执行完成,再去启动读的进程
write_queue1.join()
read_queue1.start()
管理维护进程
#1)导入模块
#2)创建进程池 multiprocessing.Pool(2) 最大允许创建2个进程
pool = multiprocessing.Pool(3)
#3、先用进程池同步方式拷贝文件
#pool.apply(函数名, (传递给函数的参数1,参数2,…))
#pool.apply(copy_work)
# 4、再用进程池异步工作方式拷贝文件
# 如果使用 apply_async 方式,需要做2点:
# 1)pool.close() 表示不在接收新的任务
# 2)主进程不在等待进程池执行结束后在退出,需要进程池join()
# pool.join() 让主进程等待进程池执行接收后再退出
pool.apply_async(copy_work)
#pool.close() 表示不在接收新的任务
pool.close()
#pool.join() 让主进程等待进程池执行接收后再退出
pool.join()
"""
思路:
1、准备两个进程
2、准备一个队列 一个进程向队列中写入数据,然后吧队列传递到另外一个进程
3、另外一个进程读取数据
"""
import time
import multiprocessing
# 1、写入数据到队列的函数
def write_queue(queue):
# for循环,向队列写入数据
for i in range(10):
# 判断队列是否已满
if queue.full():
print("队列已满!")
break
# 向队列中放入值
queue.put(i)
print("写入成功,已经写入:",i)
time.sleep(0.5)
# 2、读取队列数据并显示的函数
def read_queue(queue):
while True:
# 判断队列是否已经为空
if queue.qsize() == 0:
print("队列已空!")
break
# 从队列中读取数据
value = queue.get()
print("已经读取:", value)
if __name__ == '__main__':
# 1、创建进程池
pool = multiprocessing.Pool(2)
# 2、创建进程池中的队列
queue = multiprocessing.Manager().Queue(5)
# 3、使用进程池执行任务
# 3.1 同步方式
# pool.apply(write_queue, (queue, ))
# pool.apply(read_queue, (queue, ))
# 3.2 异步方式
# apply_async() 返回值 ApplyResult对象,该对象由一个 wait() 的方法
# wait() 方法类似join() 表示先让当前进程执行完毕,后续进程才能启动
result = pool.apply_async(write_queue, (queue, ))
result.wait()
pool.apply_async(read_queue, (queue, ))
# close()表示不再接收新的任务
pool.close()
# 主进程会等待进程池执行结束后再退出
pool.join()
迭代 —》遍历
可迭代 —》 可遍历
哪些是可遍历
列表 元组 字符串 字典 range()
不可迭代(遍历)对象
‘int’ object is not iterable
for value in 10:
print(value)
isinstance(待检测的对象, Iterable)
返回值为:True 可以迭代
False 不可以迭代
自定义一个可迭代类
class MyClass(object):
增加一个 __iter__方法
该方法就是一个迭代器
def __iter__(self):
pass
1)记录当前迭代的位置
2)配合next() 获取可迭代对象的下一个元素值
迭代器基础使用
#2、获取迭代器
l1_iterator = iter(data_list1)
#3、根据迭代器,可以获取下一个元素
value = next(l1_iterator)
print(value) # 1
自定义迭代器类,满足2点
#1)必须含有 iter()
#2) 必须含有 next()
class MyIterator(object):
def iter(self):
pass
# 当 next(迭代器) 的时候,会自动调用该方法
def __next__(self):
pass
"""
1、MyList类
1)初始化方法
2)__iter__() 方法,对外提供迭代器
3)addItem() 方法,用来添加数据
2、自定义迭代器类:MyListIterator
1) 初始化方法
2)迭代器方法 __iter__()
3) 获取下一个元素值的方法 __next__()
目标:
mylist = MyList()
for value in mylist:
print(value)
"""
# 1、MyList类
class MyList(object):
# 1)初始化方法
def __init__(self):
# 定义实例属性,保存数据
self.items = []
# 2)__iter__() 方法,对外提供迭代器
def __iter__(self):
# 创建MyListIterator 对象
mylist_iterator = MyListIterator(self.items)
# 返回迭代器
return mylist_iterator
# 3)addItem() 方法,用来添加数据
def addItem(self, data):
# 追加保存数据
self.items.append(data)
print(self.items)
# 2、自定义迭代器类:MyListIterator
class MyListIterator(object):
# 1) 初始化方法
def __init__(self, items):
# 定义实例属性,保存MyList类传递过来的items
self.items = items
# 记录迭代器迭代的位置
self.current_index = 0
# 2)迭代器方法 __iter__()
def __iter__(self):
pass
# 3) 获取下一个元素值的方法 __next__()
# next(mylist_iterator) 就会调用 __next__() 方法
def __next__(self):
# 1、 判断当前的下标是否越界
if self.current_index < len(self.items):
# 1)根据下标获取下标对应的元素值
data = self.items[self.current_index]
# 2)下标位置 +1
self.current_index += 1
# 3)返回下标对应的数据
return data
# 如果越界,直接抛出异常
else:
# raise 用于主动抛出异常
# StopIteration 停止迭代
raise StopIteration
if __name__ == '__main__':
# 1、创建自定义列表对象
mylist = MyList()
mylist.addItem("张飞")
mylist.addItem("关羽")
mylist.addItem("班长")
mylist.addItem("xxxxx")
# 遍历
# 1) iter(mylist) 获取 mylist对象的迭代器 --> MyList --> __iter__()
# 2)next(迭代器) 获取下一个值
# 3)捕获异常
# for value in mylist:
# print("name:", value)
mylist_iterator = iter(mylist)
# value = next(mylist_iterator)
# print(value)
#
# value = next(mylist_iterator)
# print(value)
#
#
# value = next(mylist_iterator)
# print(value)
#
#
# value = next(mylist_iterator)
# print(value)
#
# value = next(mylist_iterator)
# print(value)
生成器 ,是一个特殊的迭代器(保存位置+返回下一个值)
next(迭代器)得到下一个值
next(生成器) 也能够得到下一个值
生成器创建方式:
1) 列表推导式
2) 函数中使用了 yield
# 生成器的创建一:
data_list2 = (x*2 for x in range(10))
print(data_list2) #<generator object <genexpr> at 0x0000022D82CDF040>
# 通过next获取下一个值
value = next(data_list2)
print("------->", value)
方法二:函数中使用yield
# def test():
# return 10
# m = test()
# print("m = ", m) # ? 10
#
# # 使用yield 创建了一个生成器
# def test1():
# yield 10
#
# # n 是一个生成器对象
# n = test1()
# print(n)
#
# value = next(n)
# print("----->", value)
生产器注意
Return 可以停止生成器
可以通过参数去判断
协程:在不开辟新的线程的基础上,实现多个任务
协程是一个特殊的生成器
"""
1、创建 work1 的生成器
2、创建 work2 的生成器
3、获取生成器,通过next运行生成器
"""
import time
# 1、创建 work1 的生成器
def work1():
while True:
print("正在执行work1...")
yield
time.sleep(0.5)
# 2、创建 work2 的生成器
def work2():
while True:
print("正在执行work2................")
yield
time.sleep(0.5)
# 3、获取生成器,通过next运行生成器
if __name__ == '__main__':
w1 = work1()
w2 = work2()
while True:
next(w1)
next(w2)
帮我们快速实现协程
"""
greenlet 实现协程的步骤:
1、导入模块
2、创建任务 work1 work2
3、创建 greenlet 对象
4、手动 switch 任务
"""
import time
from greenlet import greenlet
# 1、创建 work1 的生成器
def work1():
while True:
print("正在执行work1...")
time.sleep(0.5)
# 切换到第二个任务
g2.switch()
# 2、创建 work2 的生成器
def work2():
while True:
print("正在执行work2................")
time.sleep(0.5)
# 切换到第一个任务
g1.switch()
if __name__ == '__main__':
# 创建greenlet的对象
# greenlet(函数名)
g1 = greenlet(work1)
g2 = greenlet(work2)
# 执行work1任务
g1.switch()
自动切换
"""
gevent 好处:能够自动识别程序中的耗时操作,在耗时的时候自动切换到其他的任务
1、导入模块
2、指派任务
"""
# 一般放到开头处
# 1、导入模块
from gevent import monkey
# 2、破解所有
monkey.patch_all()
import time
import gevent
# 1、创建 work1 的生成器
def work1():
while True:
print("正在执行work1...", gevent.getcurrent())
time.sleep(0.5)
# 默认情况下 time.sleep() 不能被gevent 识别为耗时操作
# 1) 把time.sleep() ----> gevent.sleep()
# 2) 给 gevent 打补丁(目的:让 gevent 识别 time.sleep())
# 打补丁:
# 在不修改程序源代码的情况下,为程序增加新的功能
# 如何打补丁?
# 1) 导入模块 monkey 模块 from gevent import monkey
# 2) 破解 monkey.patch_all()
# gevent.sleep(0.5)
# 2、创建 work2 的生成器
def work2():
while True:
print("正在执行work2................", gevent.getcurrent())
time.sleep(0.5)
# gevent.sleep(0.5)
if __name__ == '__main__':
# 指派任务
# gevent.spawn(函数名, 参数1,参数2,....)
g1 = gevent.spawn(work1)
g2 = gevent.spawn(work2)
# 让主线程等待携程执行完毕再退出
g1.join()
g2.join()