协程

2023-05-16

并发编程:

1. 多线程 + GIL + 共享内存

2. 多进程 + IPC (socket/ pipe)

3. 多进程 + 微线程(协程)

4. 单线程 + 异步 I/O - I/O密集型任务


使用多进程,对这种I/O密集型任务的性能,没有实质性的改善
多进程可以利用cpu的多核特性对计算密集型任务有用
对于网络爬虫这种I/O密集型任务还有一种非常好的处理方式就是单线程+异步I/O
所谓的异步I/O 就是非阻塞式的操作,也就是在I/O操作时没有终端cpu
多线程煸炒最担心什么?- 多个线程竞争资源(并发数据访问)
如果要保证数据的安全性,就需要加锁进行保护threading.Lock
acquire()/ release()
如果多个线程获取锁的方式不正确,那么就有可能导致死锁(dead lock)
加锁就意味着线程需要排队等待 相当于并发代码变成了串行执行
所以在写多线程代码时,可以尽量避开对资源的竞争
一种做法就是通过ThreadLocal为线程绑定资源让每个线程持有资源的副本  

如何让 if 和 else同时进行?

import os

pid = os.fork()
print(pid)
if pid == 0:
    print(os.getpid())
    print('hello')
else:
    print(os.getpid())
    print('goodbye')

输出结果:

协程- 

1.协程是相互协作的,协程在一个线程里面,没有进程的切换,没有竞争资源,所以就不需要加锁,可以让cpu多核特性发挥到最大。

2.协程是生成器的消费者,一个生成器,加上一个生成器的消费者,就可以构成一组相互协作的子程序。(协程是一个消费型的生成器)

3. 两个函数在互不调用的情况下相互协作。

例: 写一个协程来消费倒计时的生成器:

from time import sleep

def countdown_gen(n, consumer):
	# 预激活
	consumer.send(None)
	while n > 0:
		consumer.send(n)
		n -= 1


def countdown_con():
	while True:
		n = yield
		print('Countdown:', n)
		sleep(1)
		if n == 1:
			break

def main():
	consumer = countdown_con()
	countdown_gen(10, consumer)

if __name__ == '__main__':
	main()

这时候计时完后会出现一个 StopIteration 的异常,如果不想出现 StopIteration 这个错误,

from time import sleep

def countdown_gen(n, consumer):
	# 激活消费者协程(预激) 
	consumer.send(None)
	while n > 0:
		consumer.send(n)
		n -= 1
	try:
		consumer.send(None)
	except StopIteration:
		pass
	consumer.close()


def countdown_con():
	while True:
		n = yield
		if n:
			print('Countdown:', n)
			sleep(1)
		else:
			break


def main():
	consumer = countdown_con()
	countdown_gen(10, consumer)

if __name__ == '__main__':
	main()
模拟快递员送包裹,车子可以装多件包裹:
from time import sleep

def create_deliver_man(name, capacity=1):
	while True:
		size = 0
		buffer = []
		while size < capacity:
			pkg_name = yield
			if pkg_name:
				size += 1
				buffer.append(pkg_name)
				print('%s正在接受%s号包裹' %(name, pkg_name))
			else:
				break

		print('======%s正在派送%s件包裹========' %(name, len(buffer)))
		sleep(5)
		buffer.clear()


def create_package_center(consumer, max_packages):
	consumer.send(None)
	num = 0
	while num <= max_packages:
		print('快递中心准备派送%d号包裹' %num)
		consumer.send('包裹-%d' %num)
		num += 1

		if num % 10 == 0:
			sleep(10)
	consumer.send(None)


def main():
	dm = create_deliver_man('王大锤', 18)
	create_package_center(dm, 50)


if __name__ == '__main__':
	main()

异步 I/O

下面我们来写一段代码,用异步I/O来获取几个网站的请求头:

import asyncio
import requests


@asyncio.coroutine
def download(url):
    print('Fetch:', url)
    yield from asyncio.sleep(0.01)
    resp = requests.get(url)
    print(url, '-->', resp.status_code)
    print(url, '-->', resp.headers)


def main():
    loop = asyncio.get_event_loop()
    urls = [
        'https://www.baidu.com',
        'http://www.sohu.com',
        'http://www.sina.com.cn',
        'https://www.taobao.com',
        'http://www.qq.com'
    ]
    tasks = [download(url) for url in urls]

    loop.run_until_complete(asyncio.wait(tasks))


if __name__ == '__main__':
    main()


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

协程 的相关文章

随机推荐