异步编程是一种并行编程,其中一个工作单元被允许与主应用程序线程分开运行。 工作完成后,它将通知主线程有关工作线程的完成或失败。 使用它有很多好处,例如提高了应用程序性能和增强了响应能力。
异步性似乎是Node.js在服务器端编程中如此受欢迎的一个重要原因。 我们编写的大部分代码(尤其是在网站等大型IO应用程序中)都依赖于外部资源。 从远程数据库调用到POST到REST服务,这可以是任何东西。 一旦您请求这些资源中的任何一个,您的代码就无所事事。 使用异步编程,您可以让代码在等待其他资源响应的同时处理其他任务。
最明显的方法是使用多个进程。 在终端上,您可以启动脚本两次,三遍,四遍…十遍,然后所有脚本将独立运行或同时运行。 底层的操作系统将负责在所有这些实例之间共享您的CPU资源。 或者,您可以使用支持生成程序的多处理库,如下例所示。
from multiprocessing import Process
def print_func(continent='Asia'):
print('The name of continent is : ', continent)
if __name__ == "__main__": # confirms that the code is under main function
names = ['America', 'Europe', 'Africa']
procs = []
proc = Process(target=print_func) # instantiating without any argument
procs.append(proc)
proc.start()
# instantiating process with arguments
for name in names:
# print(name)
proc = Process(target=print_func, args=(name,))
procs.append(proc)
proc.start()
# complete the processes
for proc in procs:
proc.join()
输出
The name of continent is : Asia
The name of continent is : America
The name of continent is : Europe
The name of continent is : Africa
同时运行多件事情的下一种方法是使用线程。 线程是一条执行线,非常类似于一个进程,但是您可以在一个进程的上下文中拥有多个线程,并且它们都共享对公共资源的访问。 但是正因为如此,很难编写线程代码。 再一次,操作系统在共享CPU方面做了所有繁重的工作,但是全局解释器锁(GIL)允许一个线程在给定时间仅运行Python代码,即使您有多个线程在运行代码。 因此,在CPython中,GIL可防止多核并发。 基本上,即使您有两个或四个或更多,您也可以在单个内核中运行。
import threading
def print_cube(num):
"""
function to print cube of given num
"""
print("Cube: {}".format(num * num * num))
def print_square(num):
"""
function to print square of given num
"""
print("Square: {}".format(num * num))
if __name__ == "__main__":
# creating thread
t1 = threading.Thread(target=print_square, args=(10,))
t2 = threading.Thread(target=print_cube, args=(10,))
# starting thread 1
t1.start()
# starting thread 2
t2.start()
# wait until thread 1 is completely executed
t1.join()
# wait until thread 2 is completely executed
t2.join()
# both threads completely executed
print("Done!")
输出
Square: 100
Cube: 1000
Done!
协程是子例程的泛化。 它们用于协作式多任务处理,在协作式多任务处理中,一个进程会定期或在空闲时自动产生(放弃)控制,以使多个应用程序可以同时运行。 协程与生成器类似,但协程很少,并且我们使用yield语句的方式略有变化。 生成器产生用于迭代的数据,而协程也可以消耗数据。
def print_name(prefix):
print("Searching prefix:{}".format(prefix))
try :
while True:
# yeild used to create coroutine
name = (yield)
if prefix in name:
print(name)
except GeneratorExit:
print("Closing coroutine!!")
corou = print_name("Dear")
corou.__next__()
corou.send("James")
corou.send("Dear James")
corou.close()
输出
Searching prefix:Dear
Dear James
Closing coroutine!!
第四种方式是异步编程,其中OS不参与其中。 就操作系统而言,您将只有一个进程,并且在该进程中将只有一个线程,但是您将能够一次执行多项操作。 那么,诀窍是什么?
答案是asyncio模块,它旨在使用协程和futures来简化异步代码,并使其几乎与同步代码一样可读,因为没有回调。
接下来,我们运行3个异步任务,分别查询Reddit,提取并打印JSON。我们利用aiohttp这个http客户端库来确保HTTP请求也异步运行。
import signal
import sys
import asyncio
import aiohttp
import json
loop = asyncio.get_event_loop()
client = aiohttp.ClientSession(loop=loop)
async def get_json(client, url):
async with client.get(url) as response:
assert response.status == 200
return await response.read()
async def get_reddit_top(subreddit, client):
data1 = await get_json(client, 'https://www.reddit.com/r/' + subreddit + '/top.json?sort=top&t=day&limit=5')
j = json.loads(data1.decode('utf-8'))
for i in j['data']['children']:
score = i['data']['score']
title = i['data']['title']
link = i['data']['url']
print(str(score) + ': ' + title + ' (' + link + ')')
print('DONE:', subreddit + '\n')
def signal_handler(signal, frame):
loop.stop()
client.close()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
asyncio.ensure_future(get_reddit_top('python', client))
asyncio.ensure_future(get_reddit_top('programming', client))
asyncio.ensure_future(get_reddit_top('compsci', client))
loop.run_forever()
输出
50: Undershoot: Parsing theory in 1965 (http://jeffreykegler.github.io/Ocean-of-Awareness-blog/individual/2018/07/knuth_1965_2.html)
12: Question about best-prefix/failure function/primal match table in kmp algorithm (https://www.reddit.com/r/compsci/comments/8xd3m2/question_about_bestprefixfailure_functionprimal/)
1: Question regarding calculating the probability of failure of a RAID system (https://www.reddit.com/r/compsci/comments/8xbkk2/question_regarding_calculating_the_probability_of/)
DONE: compsci
336: /r/thanosdidnothingwrong -- banning people with python (https://clips.twitch.tv/AstutePluckyCocoaLitty)
175: PythonRobotics: Python sample codes for robotics algorithms (https://atsushisakai.github.io/PythonRobotics/)
23: Python and Flask Tutorial in VS Code (https://code.visualstudio.com/docs/python/tutorial-flask)
17: Started a new blog on Celery - what would you like to read about? (https://www.python-celery.com)
14: A Simple Anomaly Detection Algorithm in Python (https://medium.com/@mathmare_/pyng-a-simple-anomaly-detection-algorithm-2f355d7dc054)
DONE: python
1360: git bundle (https://dev.to/gabeguz/git-bundle-2l5o)
1191: Which hashing algorithm is best for uniqueness and speed? Ian Boyd's answer (top voted) is one of the best comments I've seen on Stackexchange. (https://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed)
430: ARM launches "Facts" campaign against RISC-V (https://riscv-basics.com/)
244: Choice of search engine on Android nuked by "Anonymous Coward" (2009) (https://android.googlesource.com/platform/packages/apps/GlobalSearch/+/592150ac00086400415afe936d96f04d3be3ba0c)
209: Exploiting freely accessible WhatsApp data or "Why does WhatsApp web know my phone's battery level?" (https://medium.com/@juan_cortes/exploiting-freely-accessible-whatsapp-data-or-why-does-whatsapp-know-my-battery-level-ddac224041b4)
DONE: programming
此外,在某些情况下,您可能需要在不同服务器之间分配任务。 在这种情况下,我们可以利用RQ(Redis Queue)。 这是一个简单的Python库,用于对作业进行排队并与工作人员在后台进行处理。 它由Redis(键/值数据存储)支持。
在下面的示例中,我们使用Redis将简单的函数count_words_at_url排队。
from mymodule import count_words_at_url
from redis import Redis
from rq import Queue
q = Queue(connection=Redis())
job = q.enqueue(count_words_at_url, 'http://nvie.com')
******mymodule.py******
import requests
def count_words_at_url(url):
"""Just an example function that's called async."""
resp = requests.get(url)
print( len(resp.text.split()))
return( len(resp.text.split()))
输出
15:10:45 RQ worker 'rq:worker:EMPID18030.9865' started, version 0.11.0
15:10:45 *** Listening on default...
15:10:45 Cleaning registries for queue: default
15:10:50 default: mymodule.count_words_at_url('http://nvie.com') (a2b7451e-731f-4f31-9232-2b7e3549051f)
322
15:10:51 default: Job OK (a2b7451e-731f-4f31-9232-2b7e3549051f)
15:10:51 Result is kept for 500 seconds
详情参阅http://viadean.com/py_async.html