Python 支持多进程,多线程,以及协程,但是有 GIL 全局锁的限制,Python 只能运行一个系统级线程,无法利用多核 CPU。所以 Python 中的多线程,协程只适合 IO 密集型任务,而不适合 CPU 密集型任务。在需要执行 CPU 密集型任务是推荐采用多进程,或是使用 C 语言扩展。
多进程
Python
的``multiprocessing`模块提供了多进程接口。实例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
import multiprocessing, time
def countdown(n):
while n > 0:
print('T-minus', n)
n -= 1
time.sleep(3)
if __name__ == "__main__":
#Creat and lanch a process, must be in __main__
p = multiprocessing.Process(target=countdown, args=(10,))
p.start()
|
多线程
Python
的threading
模块提供了多线程接口,接口与多进程类似。
1
2
3
4
5
6
7
8
9
10
11
|
import time
def countdown(n):
while n > 0:
print('T-minus', n)
n -= 1
time.sleep(3)
# Create and launch a thread
from threading import Thread
t = Thread(target=countdown, args=(10,))
t.start()
|
协程
协程可以说是轻量级线程,但是协程可以在运行到一半时暂时挂起,转而去执行其他的程序,再在适当的适合返回继续执行。
协程实现消费者,生产者模型(参考廖雪峰教程)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer()
produce(c)
|
输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK
|
这里的consumer
函数是一个generator
,把一个consumer
传入produce
后:
-
首先调用c.send(None)
启动生成器;
-
然后,一旦生产了东西,通过c.send(n)
切换到consumer
执行;
-
consumer
通过yield
拿到消息,处理,又通过yield
把结果传回;
-
produce
拿到consumer
处理的结果,继续生产下一条消息;
-
produce
决定不生产了,通过c.close()
关闭consumer
,整个过程结束。
整个流程无锁,由一个线程执行,produce
和consumer
协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
asycnio
asyncio
是 Python 3.4 版本引入的标准库,直接内置了对异步 IO 的支持
asyncio
的编程模型就是一个消息循环。我们从asyncio
模块中直接获取一个EventLoop
的引用,然后把需要执行的协程扔到EventLoop
中执行,就实现了异步 IO
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import asyncio
async def hello():
print("Hello world!")
# 异步调用 asyncio.sleep(1):
r = await asyncio.sleep(1)
print("Hello again!")
# 获取 EventLoop:
loop = asyncio.get_event_loop()
# 执行 coroutine
loop.run_until_complete(hello())
loop.close()
|
aiohttp
aiohttp
是基于asyncio
实现的 HTTP 框架
利用aiohttp
异步抓取网页:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import asyncio
import aiohttp
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64;\
x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36'}
async def get_html(url):
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers) as resp:
print(await resp.text())
loop = asyncio.get_event_loop()
tasks = [get_html(url) for url in ['http://www.sina.com.cn', 'http://www.sohu.com', 'http://www.163.com']]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
|
aiomysql
aiomyql
是基于asyncio
实现的异步操作MySQL
数据库的框架
example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import asyncio
import aiomysql
loop = asyncio.get_event_loop()
@asyncio.coroutine
def test_example():
conn = yield from aiomysql.connect(host='127.0.0.1', port=3306,
user='root', password='', db='mysql',
loop=loop)
cur = yield from conn.cursor()
yield from cur.execute("SELECT Host,User FROM user")
print(cur.description)
r = yield from cur.fetchall()
print(r)
yield from cur.close()
conn.close()
loop.run_until_complete(test_example())
|
Author
Hao
LastMod
2017-10-20
License
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可