Sök…


Coroutine och delegationssyntax

Innan Python 3.5+ släpptes asyncio modulen generatorer för att härma asynkrona samtal och hade därmed en annan syntax än den nuvarande Python 3.5-utgåvan.

Python 3.x 3.5

Python 3.5 introducerade async och await nyckelord. Observera avsaknaden av parenteser runt det await func() samtalet.

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())
Python 3.x 3.3 3.5

Före Python 3.5 användes @asyncio.coroutine dekoratören för att definiera en koroutin. Utbytet från uttrycket användes för generatordelegering. Notera parenteserna kring 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())
Python 3.x 3.5

Här är ett exempel som visar hur två funktioner kan köras asynkront:

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)

Asynkrona körare

Obs: Använder syntaks Python 3.5+ async / invänta

asyncio stöder användningen av Executor objekt som finns i concurrent.futures för att schemalägga uppgifter asynkront. Event-loopar har funktionen run_in_executor() som tar ett Executor objekt, en Callable och Callable-parametrarna.

Schemalägga en uppgift för en 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))

Varje händelsslinga har också en "standard" Executor plats som kan tilldelas en Executor . För att tilldela en Executor och schemalägga uppgifter från slingan använder set_default_executor() metoden 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))

Det finns två huvudtyper av Executor i concurrent.futures , ThreadPoolExecutor och ProcessPoolExecutor . ThreadPoolExecutor innehåller en pool av trådar som antingen kan ställas in manuellt på ett specifikt antal trådar genom konstruktören eller som standard är antalet kärnor på maskintiderna. ThreadPoolExecutor använder poolen av trådar för att utföra uppgifter som tilldelats den och är i allmänhet bättre vid CPU-bundna operationer snarare än I / O-bundna operationer. Kontrast det till ProcessPoolExecutor som skapar en ny process för varje uppgift som tilldelats den. ProcessPoolExecutor kan bara ta uppgifter och parametrar som är plockbara. De vanligaste icke-plockbara uppgifterna är föremålens metoder. Om du måste schemalägga ett objekts metod som en uppgift i en Executor måste du använda en ThreadPoolExecutor .

Använda UVLoop

uvloop är en implementering för asyncio.AbstractEventLoop baserat på libuv (Används av nodejs). Den överensstämmer med 99% av asyncio funktionerna och är mycket snabbare än den traditionella asyncio.EventLoop . uvloop är för närvarande inte tillgängligt på Windows, installera det med pip install uvloop .

import asyncio
import uvloop

if __name__ == "__main__":
    asyncio.set_event_loop(uvloop.new_event_loop())
    # Do your stuff here ...

Man kan också ändra EventLoopPolicy genom att ställa EventLoopPolicy till den i uvloop .

import asyncio
import uvloop

if __name__ == "__main__":
    asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
    loop = asyncio.new_event_loop()

Synkronisering Primitiv: Händelse

Begrepp

Använd en Event att synkronisera schemaläggningen för flera koroutiner .

Enkelt uttryckt är att en händelse är som pistolskottet i en löpning: den låter löparna utanför startblocken.

Exempel

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)

Produktion:

Konsument B väntar
Konsument A väntar
EVENT SET
Konsument B utlöses
Konsument A utlöses

En enkel Websocket

Här skapar vi ett enkelt eko-nätuttag med asyncio . Vi definierar koroutiner för att ansluta till en server och skicka / ta emot meddelanden. Kommuniceringarna av webbuttaget körs i en main coroutine, som drivs av en händelsslinga. Detta exempel modifieras från ett tidigare inlägg .

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())

Vanlig missuppfattning om asyncio

förmodligen den vanligaste missuppfattningen om asnycio är att den låter dig köra vilken som helst uppgift parallellt - att ta bort GIL (global tolklås) och därför utföra blockeringsjobb parallellt (på separata trådar). det gör det inte !

asyncio (och bibliotek som är byggda för att samarbeta med asyncio ) bygger på koroutiner: funktioner som (i samarbete) ger kontrollflödet tillbaka till den uppringande funktionen. notera asyncio.sleep i exemplen ovan. detta är ett exempel på en icke-blockerande koroutin som väntar "i bakgrunden" och ger kontrollflödet tillbaka till den samtalande funktionen (när den kallas med await ). time.sleep är ett exempel på en blockeringsfunktion. programmets körningsflöde stannar bara där och kommer bara tillbaka efter att time.sleep har avslutats.

ett verkligt exempel är requests som består (för tillfället) endast på blockeringsfunktioner. det finns ingen samtidighet om du kallar någon av dess funktioner inom asyncio . aiohttp å andra sidan byggdes med asyncio i åtanke. dess koroutiner kommer att köras samtidigt.

  • om du har långvariga CPU-bundna uppgifter vill du köra parallellt asyncio inte är för dig. för det behöver du threads eller multiprocessing .

  • Om du har IO-bundna jobb som kör, kan du köra dem samtidigt med hjälp av asyncio .



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow