from threading import Thread
t = Thread()
# 功能:创建线程对象
# 参数:target 绑定线程函数
# args 元组 给线程函数位置传参
# kwargs 字典 给线程函数键值传参
t.start() # 启动线程
t.join([timeout]) # 回收线程
import threading
from time import sleep,ctime
loops=[4,2]
def loop(nloop,nsec):
print('start loop',nloop ,' at:',ctime())
sleep(nsec)
print('loop',nloop ,'done at:',ctime())
def main():
threads=[]
nloops = range(len(loops))
for i in nloops:
# 注意传参
t = threading.Thread(target=loop,args=(i,loops[i]))
threads.append(t)
for in in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
if __name__ == '__main__':
main()
import threading
from time import sleep,ctime
loops=[4,2]
class MyThread(threading.Thread):
def __init__(self,func,args,name=''):
threading.Thread.__init__(self)
self.func = func
self.args = args
self.name = name
def run(self):
self.func(*self.args)
def loop(nloop,nsec):
print('start loop',nloop ,' at:',ctime())
sleep(nsec)
print('loop',nloop ,'done at:',ctime())
def main():
threads=[]
nloops = range(len(loops))
for i in nloops:
t = MyThread(loop,(i,loops[i]),loop.__name__)
threads.append(t)
for in in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
if __name__ == '__main__':
main()
线程之间用于交互的一个对象,这个event是一个内部的标签,线程可以等待这个标签的状态
如果标签(event)没有设置,则线程会一直等待(此时wait是阻塞模式)
import threading
# 创建线程event对象
e = threading.Event()
# 设置e, 使wait结束阻塞
e.wait([timeout])
# 阻塞等待e被set
e.set()
# 使e回到未被设置状态
e.clear()
# 查看当前e是否被设置
e.is_set()
import threading
import time
import random
# 先定义一个灯的函数,由灯的线程去调用这个函数,绿灯是允许通过,红灯是不允许通过,其中绿灯10s,红灯也是10s,这里就要用到事件
# 如果是绿灯,则设置事件的状态,如果是红灯,则取消事件的状态,初始状态为红灯
def light(event):
if event.is_set():
#设置初始状态,该初始状态为事件没有状态,则进程一直会阻塞
event.clear()
count = 0
while True:
#进入交通灯灯循环的状态
if count < 10:
print("----red light on----")
elif count < 20:
print("----green light on----")
if not event.is_set():
event.set()
else:
pass
else:
count = 0
if event.is_set():
event.clear()
count += 1
time.sleep(1)
# 在定义一个车的函数,由车的线程去调用这个函数
def car(n,event):
while True:
time.sleep(1)
if event.is_set():
print("car [%(name)s] is running" %{"name":n})
else:
print("car [%(name)s] is waiting for red light" % {"name": n})
event.wait()
if __name__ == '__main__':
event = threading.Event()
light_thread = threading.Thread(target=light,args=(event,))
light_thread.start()
for i in range(6):
t = threading.Thread(target=car, args=[i,event])
t.start()
lock.acquire() 上锁 如果lock已经上锁再调用会阻塞
lock.release() 解锁
with lock: 上锁
…
…
with 代码块解锁自动解锁
from threading import Thread, Lock
a = b = 0
lock = Lock()
def value():
while True:
# 上锁
lock.acquire()
print('a=%d,b=%d' % (a, b)) if a != b else print('a不等于b')
# 解锁
lock.release()
t = Thread(target=value)
t.start()
while True:
# with 开始上锁
with lock:
a += 1
b += 1
# with 解锁 自动解锁
t.join()
print('程序结束')
协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。
协程的标准定义:
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此,协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。线程是CPU控制的,而协程是程序自身控制的。
Python中的生成器本身就具有状态保存和恢复的功能,非常适合用来实现协程。生成器协程的最大特点在于使用yield语句来挂起函数并保存函数状态。
def simple_corouctine():
print('-> corouctine started')
x = yield # yeild接收my_continue.send发送的信息
print('-> corouctine received:',x)
# 调用协程
my_corouctine = simple_corouctine()
print(my_corouctine)
# 先启动协程,让协程处于暂停状态
next(my_corouctine) # 程序通过调用方法next()将生成器推进到第一个yield语句处
# 发送消息给协程,回复执行,打印消息
my_corouctine.send('hello world')
在Python 3.5版本以后,Python提供了原生的支持协程的语法async/await。它提供了更简洁的语法,更好的可读性。
import asyncio
async def countdown(n):
while n >0 :
print(f'T-minux {n}')
await asyncio.sleep(1)
# yield from asyncio.sleep(1) # 效果同上
n -=1
loop = asyncio.get_event_loop()
loop.run_until_complete(countdown(5))
import asyncio
async def countdown(n,msg):
while n >0 :
print(f'{msg} - {n}')
await asyncio.sleep(1)
# yield from asyncio.sleep(1) # 效果同上
n -=1
task_list = [
asyncio.ensure_future(countdown(5,'A')),
asyncio.ensure_future(countdown(2,'B')),
asyncio.ensure_future(countdown(3,'C'))
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(task_list))
获取返回值
import asyncio
async def firstCorouctine(): # 定义协程
await asyncio.sleep(1)
return '34434'
# coroutine :协同程序
coroutine = firstCorouctine() # 将协程赋值给coroutine
task = asyncio.ensure_future(coroutine) # 封装为task
loop = asyncio.get_event_loop() # 获取事件循环
loop.run_until_complete(task) # 执行
return_value = task.result() # 获取协程返回值
print('协程返回的值为:{}'.format(return_value))
gevent是一个基于协程的Python网络库,它提供了一种基于yield的编程模型,允许程序员编写异步,非阻塞代码,且易于编写和测试。
安装gevent:pip install gevent
import gevent
def func1():
print('func1 begin')
gevent.sleep(3)
print('func1 end')
def func2():
print('func2 begin')
gevent.sleep(2)
print('func2 end')
def func3(no:int):
print('func3 begin')
gevent.sleep(2)
print('func3 end')
g1 = gevent.spawn(func1)
g2 = gevent.spawn(func2)
g3 = gevent.spawn(func3,3) # 传参
gevent.joinall([g1, g2, g3])
在开头导入monkey.patch_all(),非常重要,会自动将 python 的一些标准模块替换成 gevent 框架,这个补丁其实存在着一些坑:
在实际情况中协程和进程的组合非常常见,两个结合可以大幅提升性能,但直接使用猴子补丁会导致进程运行出现问题;将 thread 置成 False可以解决该问题,缺点是无法发挥 monkey.patch_all() 的全部性能。
import gevent
from gevent import monkey
monkey.patch_all(thread=False, socket=False, select=False)
协程虽然是轻量级线程,但并发数达到一定量级后,会把系统的文件句柄数占光。所以需要通过 Pool 来限制并发数。
import gevent
from gevent.pool import Pool
from gevent import monkey
monkey.patch_all()
import time,datetime
def test(tm):
time.sleep(tm)
print('时间:{}'.format(datetime.datetime.now()))
if __name__ =='__main__':
task = []
# 限制最大并发
pool = Pool(5)
# 分配100个任务,最大并发数为5
for i in range(100):
task.append(pool.spawn(test,5))
gevent.joinall(task)
gevent 虽然在编程方面很方便,开头使用 monkey.patch_all(),就能让你的同步代码享受到异步的性能。但坑也是存在的,所以复杂的业务场景不推荐使用 gevent,可以使用python 标准库中的 asyncio。