文章目录
- python协程编程与多线程
- 一、python协程编程
- 1.1 同步和异步的区别
- 1.2 普通函数、生成器函数、异步函数、异步生成器
- 1.2 async
- 1.3 await
- 1.4 例子
- 1.5 异步生成器AsyncGenerator
- 1.6 async可修饰
- 二、python多线程
- 2.1多线程的优点
- 2.2 python中threading模块
- 2.2.1 普通创建
- 2.2.2 自定义创建
- 2.2.3 守护线程
- 2.2.4 主线程等待子线程
- 2.2.5 线程之间共享全局变量
- 2.2.6 互斥锁
- 2.2.7 递归锁
- 2.2.8 信号量(BoundedSemaphore类)
- 2.2.9 事件(Event类)
python协程编程与多线程
一、python协程编程
1.1 同步和异步的区别
同步:所有的操作都做完,才返回给用户。这样用户在线等待的时间太长,给用户一种卡死了的感觉(就是系统迁移中,点击了迁移,界面就不动了,但是程序还在执行,卡死了的感觉)。这种情况下,用户不能关闭界面,如果关闭了,即迁移程序就中断了。(必须等)
异步:将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。但是用户没有卡死的感觉,会告诉你,你的请求系统已经响应了。你可以关闭界面了。(不用等,可以做其他事)
主要使用:async/await、import asyncio
1.2 普通函数、生成器函数、异步函数、异步生成器
普通函数
def function():
return 1
生成器函数
def generator():
yield 1
异步函数
async def async_function():
return 1
异步生成器
async def async_generator():
yield 1
返回类型
import types
print(type(function) is types.FunctionType)
print(type(generator()) is types.GeneratorType)
print(type(async_function()) is types.CoroutineType)
print(type(async_generator()) is types.AsyncGeneratorType)
1.2 async
#直接调用异步函数,只会返回一个coroutine对象。
print(async_function())
# <coroutine object async_function at 0x102ff67d8>
# 使用这个协程对象的send方法给协程发送一个值
print(async_function().send(None))
# StopIteration: 1
#
try:
async_function().send(None)
except StopIteration as e:
print(e.value)
# 1
- coroutine类
class Coroutine(Awaitable):
__slots__ = ()
@abstractmethod
def send(self, value):
...
@abstractmethod
def throw(self, typ, val=None, tb=None):
...
def close(self):
...
@classmethod
def __subclasshook__(cls, C):
if cls is Coroutine:
return _check_methods(C, '__await__', 'send', 'throw', 'close')
return NotImplemented
1.3 await
await语法来挂起自身的协程,并等待另一个协程完成直到返回结果。
- await语法只能出现在通过async修饰的函数中,否则会报SyntaxError错误。
- await后面的对象需要是一个Awaitable,或者实现了相关的协议。
- 只要一个类实现了__await__方法,那么通过它构造出来的实例就是一个Awaitable:
class Awaitable(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __await__(self):
yield
@classmethod
def __subclasshook__(cls, C):
if cls is Awaitable:
return _check_methods(C, "__await__")
return NotImplemented
1.4 例子
- 超市货架创建:
class Potato:
@classmethod
def make(cls, num, *args, **kws):
potatos = []
for i in range(num):
potatos.append(cls.__new__(cls, *args, **kws))
return potatos
all_potatos = Potato.make(5)
- 从超市货架取货:货架土豆不够,只能等待
def take_potatos(num):
count = 0
while True:
if len(all_potatos) == 0:
sleep(.1)
else:
potato = all_potatos.pop()
yield potato
count += 1
if count == num:
break
def buy_potatos():
bucket = []
for p in take_potatos(50):
bucket.append(p)
- 使用异步函数
async def take_potatos(num):
count = 0
while True:
if len(all_potatos) == 0:
await ask_for_potato()
potato = all_potatos.pop()
yield potato
count += 1
if count == num:
break
async def ask_for_potato():
await asyncio.sleep(random.random())
all_potatos.extend(Potato.make(random.randint(1, 10)))
async def buy_potatos():
bucket = []
async for p in take_potatos(50):
bucket.append(p)
print(f'Got potato {id(p)}...')
#async for语法表示我们要后面迭代的是一个异步生成器
def main():
import asyncio
loop = asyncio.get_event_loop()
res = loop.run_until_complete(buy_potatos())
loop.close()
# 可以买土豆,如果土豆没了,不用死等,可以买番茄
def main():
import asyncio
loop = asyncio.get_event_loop()
res = loop.run_until_complete(asyncio.wait([buy_potatos(), buy_tomatos()]))
loop.close()
1.5 异步生成器AsyncGenerator
- python3.6版本以后
class AsyncGenerator(AsyncIterator):
__slots__ = ()
async def __anext__(self):
...
@abstractmethod
async def asend(self, value):
...
@abstractmethod
async def athrow(self, typ, val=None, tb=None):
...
async def aclose(self):
...
@classmethod
def __subclasshook__(cls, C):
if cls is AsyncGenerator:
return _check_methods(C, '__aiter__', '__anext__',
'asend', 'athrow', 'aclose')
return NotImplemented
- 异步推导式
bucket = [p async for p in take_potatos(50)]
1.6 async可修饰
- 除了函数之外,类实例的普通方法、类方法也能用async语法修饰。且实例方法的调用同样是返回一个coroutine。
- async也可以应用到上下文管理器中,__aenter__和__aexit__需要返回一个Awaitable。
二、python多线程
2.1多线程的优点
多线程编程具有如下几个优点:
- 进程之间不能共享内存,但线程之间共享内存非常容易。共享内存相互之间更易通信。
- 操作系统在创建进程时,需要为该进程重新分配系统资源,但创建线程的代价则小得多。因此,使用多线程来实现多任务并发执行比使用多进程的效率高。
- Python 语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了 Python 的多线程编程。
2.2 python中threading模块
2.2.1 普通创建
import threading
import time
def run(n):
print("task", n)
time.sleep(1)
print('2s')
time.sleep(1)
print('1s')
time.sleep(1)
print('0s')
time.sleep(1)
if __name__ == '__main__':
t1 = threading.Thread(target=run, args=("t1",))
t2 = threading.Thread(target=run, args=("t2",))
t1.start()
t2.start()
2.2.2 自定义创建
import threading
import time
class MyThread(threading.Thread):
def __init__(self, n):
super(MyThread, self).__init__() # 重构run函数必须要写
self.n = n
def run(self):
print("task", self.n)
time.sleep(1)
print('2s')
time.sleep(1)
print('1s')
time.sleep(1)
print('0s')
time.sleep(1)
if __name__ == "__main__":
t1 = MyThread("t1")
t2 = MyThread("t2")
t1.start()
t2.start()
2.2.3 守护线程
- 使用setDaemon(True)把所有的子线程都变成了主线程的守护线程
- 当主进程结束后,子线程也会随之结束。
import threading
import time
def run(n):
print("task", n)
time.sleep(1) #此时子线程停1s
print('3')
time.sleep(1)
print('2')
time.sleep(1)
print('1')
if __name__ == '__main__':
t = threading.Thread(target=run, args=("t1",))
t.setDaemon(True) #把子进程设置为守护线程,必须在start()之前设置
t.start()
print("end")
# main为主线程
----------------------------------
>>> task t1
>>> end
2.2.4 主线程等待子线程
import threading
import time
def run(n):
print("task", n)
time.sleep(1) #此时子线程停1s
print('3')
time.sleep(1)
print('2')
time.sleep(1)
print('1')
if __name__ == '__main__':
t = threading.Thread(target=run, args=("t1",))
t.setDaemon(True) #把子进程设置为守护线程,必须在start()之前设置
t.start()
t.join() # 设置主线程等待子线程结束
print("end")
2.2.5 线程之间共享全局变量
- 线程是进程的执行单元,进程是系统分配资源的最小单位,所以在同一个进程中的多线程是共享资源的。
import threading
import time
g_num = 100
def work1():
global g_num
for i in range(3):
g_num += 1
print("in work1 g_num is : %d" % g_num)
def work2():
global g_num
print("in work2 g_num is : %d" % g_num)
if __name__ == '__main__':
t1 = threading.Thread(target=work1)
t1.start()
time.sleep(1)
t2 = threading.Thread(target=work2)
t2.start()
2.2.6 互斥锁
防止不同线程,同时对一个变量进行修改
from threading import Thread,Lock
import os,time
def work():
global n
lock.acquire()
temp=n
time.sleep(0.1)
n=temp-1
lock.release()
if __name__ == '__main__':
lock=Lock()
n=100
l=[]
for i in range(100):
p=Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
2.2.7 递归锁
RLcok类的用法和Lock类一模一样,但它支持嵌套,在多个锁没有释放的时候一般会使用RLcok类。
import threading
import time
def Func(lock):
global gl_num
lock.acquire()
gl_num += 1
time.sleep(1)
print(gl_num)
lock.release()
if __name__ == '__main__':
gl_num = 0
lock = threading.RLock()
for i in range(10):
t = threading.Thread(target=Func, args=(lock,))
t.start()
2.2.8 信号量(BoundedSemaphore类)
互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
import threading
import time
def run(n, semaphore):
semaphore.acquire() #加锁
time.sleep(1)
print("run the thread:%s\n" % n)
semaphore.release() #释放
if __name__ == '__main__':
num = 0
semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行
for i in range(22):
t = threading.Thread(target=run, args=("t-%s" % i, semaphore))
t.start()
while threading.active_count() != 1:
pass # print threading.active_count()
else:
print('-----all threads done-----')
2.2.9 事件(Event类)
python线程的事件用于主线程控制其他线程的执行,事件是一个简单的线程同步对象,其主要提供以下几个方法:
- clear 将flag设置为“False”
- set 将flag设置为“True”
- is_set 判断是否设置了flag
- wait 会一直监听flag,如果没有检测到flag就一直处于阻塞状态
事件处理的机制:全局定义了一个“Flag”,当flag值为“False”,那么event.wait()就会阻塞,当flag值为“True”,那么event.wait()便不再阻塞。
#利用Event类模拟红绿灯
import threading
import time
event = threading.Event()
def lighter():
count = 0
event.set() #初始值为绿灯
while True:
if 5 < count <=10 :
event.clear() # 红灯,清除标志位
print("\33[41;1mred light is on...\033[0m")
elif count > 10:
event.set() # 绿灯,设置标志位
count = 0
else:
print("\33[42;1mgreen light is on...\033[0m")
time.sleep(1)
count += 1
def car(name):
while True:
if event.is_set(): #判断是否设置了标志位
print("[%s] running..."%name)
time.sleep(1)
else:
print("[%s] sees red light,waiting..."%name)
event.wait()
print("[%s] green light is on,start going..."%name)
light = threading.Thread(target=lighter,)
light.start()
car = threading.Thread(target=car,args=("MINI",))
car.start()