并发编程:
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()