Python Language
Asyncio-Modul
Suche…
Coroutine und Delegation Syntax
Vor der Veröffentlichung von Python 3.5+ verwendete das asyncio
Modul Generatoren, um asynchrone Aufrufe nachzuahmen, und verfügte daher über eine andere Syntax als das aktuelle Python 3.5-Release.
In Python 3.5 wurden die async
und await
Schlüsselwörter eingeführt. Beachten Sie das Fehlen von Klammern um den Aufruf von await func()
.
import asyncio
async def main():
print(await func())
async def func():
# Do time intensive stuff...
return "Hello, world!"
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Vor Python 3.5 wurde der Dekorateur @asyncio.coroutine
verwendet, um eine Coroutine zu definieren. Die Ausbeute aus Ausdruck wurde für die Generatordelegierung verwendet. Beachten Sie die Klammern um den yield from func()
.
import asyncio
@asyncio.coroutine
def main():
print((yield from func()))
@asyncio.coroutine
def func():
# Do time intensive stuff..
return "Hello, world!"
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Hier ein Beispiel, das zeigt, wie zwei Funktionen asynchron ausgeführt werden können:
import asyncio
async def cor1():
print("cor1 start")
for i in range(10):
await asyncio.sleep(1.5)
print("cor1", i)
async def cor2():
print("cor2 start")
for i in range(15):
await asyncio.sleep(1)
print("cor2", i)
loop = asyncio.get_event_loop()
cors = asyncio.wait([cor1(), cor2()])
loop.run_until_complete(cors)
Asynchrone Executoren
Hinweis: Verwendet die Python 3.5+ async / await-Syntax
asyncio
unterstützt die Verwendung von in concurrent.futures
gefundenen Executor
Objekten, um Aufgaben asynchron zu planen. Ereignisschleifen haben die Funktion run_in_executor()
die ein Executor
Objekt, ein Callable
und die Callable-Parameter übernimmt.
Planen einer Aufgabe für einen Executor
import asyncio
from concurrent.futures import ThreadPoolExecutor
def func(a, b):
# Do time intensive stuff...
return a + b
async def main(loop):
executor = ThreadPoolExecutor()
result = await loop.run_in_executor(executor, func, "Hello,", " world!")
print(result)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
Jede Ereignisschleife verfügt außerdem über einen "Standard" Executor
Slot, der einem Executor
zugewiesen werden kann. Um einen Executor
zuzuweisen und Aufgaben aus der Schleife zu planen, verwenden Sie die Methode set_default_executor()
.
import asyncio
from concurrent.futures import ThreadPoolExecutor
def func(a, b):
# Do time intensive stuff...
return a + b
async def main(loop):
# NOTE: Using `None` as the first parameter designates the `default` Executor.
result = await loop.run_in_executor(None, func, "Hello,", " world!")
print(result)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.set_default_executor(ThreadPoolExecutor())
loop.run_until_complete(main(loop))
Es gibt zwei Haupttypen von Executor
in concurrent.futures
, den ThreadPoolExecutor
und den ProcessPoolExecutor
. Der ThreadPoolExecutor
enthält einen Pool von Threads , die entweder manuell auf eine bestimmte Anzahl von Fäden durch den Konstruktor oder Standardwerte für die Anzahl der Kerne auf dem Maschinenzeit 5. eingestellt werden ThreadPoolExecutor
verwendet den Pool von Threads Aufgaben es auszuführen zugeordnet ist und Im Allgemeinen besser bei CPU-gebundenen Operationen als bei E / A-gebundenen Operationen. ProcessPoolExecutor
dies mit dem ProcessPoolExecutor
der für jede ihm zugewiesene Aufgabe einen neuen Prozess erzeugt. Der ProcessPoolExecutor
kann nur Aufgaben und Parameter übernehmen, die kommissionierbar sind. Die häufigsten nicht auswählbaren Aufgaben sind die Methoden von Objekten. Wenn Sie die Methode eines Objekts als Task in einem Executor
ThreadPoolExecutor
müssen, müssen Sie einen ThreadPoolExecutor
.
UVLoop verwenden
uvloop
ist eine Implementierung für asyncio.AbstractEventLoop
basierend auf libuv (Wird von nodejs verwendet). Es ist mit 99% der asyncio
Funktionen asyncio
und ist viel schneller als das herkömmliche asyncio.EventLoop
. uvloop
ist derzeit unter Windows nicht verfügbar. Installieren Sie es mit pip install uvloop
.
import asyncio
import uvloop
if __name__ == "__main__":
asyncio.set_event_loop(uvloop.new_event_loop())
# Do your stuff here ...
Sie können die Ereignisschleifen-Factory auch ändern, indem Sie die EventLoopPolicy
auf die in uvloop
.
import asyncio
import uvloop
if __name__ == "__main__":
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
loop = asyncio.new_event_loop()
Synchronisationsprimitiv: Ereignis
Konzept
Verwenden Sie ein Event
, um die Planung mehrerer Coroutinen zu synchronisieren .
Vereinfacht gesagt, ist ein Ereignis wie eine Waffe, die auf ein Rennen abgefeuert wird: Es lässt die Läufer die Startblöcke verlassen.
Beispiel
import asyncio
# event trigger function
def trigger(event):
print('EVENT SET')
event.set() # wake up coroutines waiting
# event consumers
async def consumer_a(event):
consumer_name = 'Consumer A'
print('{} waiting'.format(consumer_name))
await event.wait()
print('{} triggered'.format(consumer_name))
async def consumer_b(event):
consumer_name = 'Consumer B'
print('{} waiting'.format(consumer_name))
await event.wait()
print('{} triggered'.format(consumer_name))
# event
event = asyncio.Event()
# wrap coroutines in one future
main_future = asyncio.wait([consumer_a(event),
consumer_b(event)])
# event loop
event_loop = asyncio.get_event_loop()
event_loop.call_later(0.1, functools.partial(trigger, event)) # trigger event in 0.1 sec
# complete main_future
done, pending = event_loop.run_until_complete(main_future)
Ausgabe:
Verbraucher B wartet
Verbraucher Ein Warten
EVENT SET
Verbraucher B ausgelöst
Consumer A ausgelöst
Ein einfacher Websocket
Hier asyncio
wir mit asyncio
einen einfachen Echo- asyncio
. Wir definieren Coroutinen für die Verbindung zu einem Server und zum Senden / Empfangen von Nachrichten. Die Communcations des websocket werden in einem laufen main
Koroutine, die durch eine Ereignisschleife ausgeführt wird. Dieses Beispiel wurde aus einem früheren Beitrag geändert.
import asyncio
import aiohttp
session = aiohttp.ClientSession() # handles the context manager
class EchoWebsocket:
async def connect(self):
self.websocket = await session.ws_connect("wss://echo.websocket.org")
async def send(self, message):
self.websocket.send_str(message)
async def receive(self):
result = (await self.websocket.receive())
return result.data
async def main():
echo = EchoWebsocket()
await echo.connect()
await echo.send("Hello World!")
print(await echo.receive()) # "Hello World!"
if __name__ == '__main__':
# The main loop
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Häufiges Missverständnis über Asyncio
Das häufigste Missverständnis über asnycio
besteht asnycio
darin, dass Sie alle Aufgaben parallel ausführen können, indem Sie die GIL (globale Interpretersperre) umgehen und parallel dazu blockierende Jobs (in separaten Threads) ausführen. es tut nicht
asyncio
(und Bibliotheken, die mit asyncio
) bauen auf Coroutines auf: Funktionen, die (gemeinsam) den Steuerungsfluss an die aufrufende Funktion zurückgeben. Beachten Sie asyncio.sleep
in den obigen Beispielen. Dies ist ein Beispiel für eine nicht blockierende Coroutine, die 'im Hintergrund' wartet und den Steuerungsfluss an die aufrufende Funktion zurückgibt (wenn mit await
aufgerufen). time.sleep
ist ein Beispiel für eine time.sleep
. Der Ablauf des Programms wird dort einfach time.sleep
und erst nach time.sleep
.
Ein reales Beispiel ist die requests
die (vorerst) nur aus Sperrfunktionen besteht. Es gibt keine Parallelität, wenn Sie eine seiner Funktionen innerhalb von asyncio
. aiohttp
dagegen wurde für asyncio
. Seine Coroutinen werden gleichzeitig laufen.
Wenn Sie langlaufende CPU-gebundene Aufgaben haben, die Sie parallel
asyncio
istasyncio
nichts für Sie. dazu benötigen Siethreads
odermultiprocessing
.wenn Sie IO-gebundene Arbeitsplätze ausgeführt wird , können Sie sie gleichzeitig mit laufen
asyncio
.