Išsamus Python asyncio korutinų derinimo vadovas, naudojant integruotą derinimo režimą. Sužinokite, kaip nustatyti ir išspręsti dažnas asinchroninio programavimo problemas, kad sukurtumėte patikimas programas.
Python Coroutine Derinimas: Asyncio Derinimo Režimo Įvaldymas
Asinchroninis programavimas su asyncio
Python kalba siūlo didelį našumo pagerėjimą, ypač operacijoms, susijusioms su įvesties/išvesties (I/O) operacijomis. Tačiau derinti asinchroninį kodą gali būti sudėtinga dėl jo netiesinio vykdymo srauto. Python suteikia įtaisytąjį derinimo režimą asyncio
, kuris gali labai supaprastinti derinimo procesą. Šiame vadove bus nagrinėjama, kaip efektyviai naudoti asyncio
derinimo režimą, norint nustatyti ir išspręsti dažnas problemas jūsų asinchroninėse programose.
Asinchroninio Programavimo Iššūkių Supratimas
Prieš pradedant gilintis į derinimo režimą, svarbu suprasti dažnus iššūkius, susijusius su asinchroninio kodo derinimu:
- Netiesinis Vykdymas: Asinchroninis kodas nevykdomas nuosekliai. Korutinos grąžina valdymą atgal į įvykių ciklą, todėl sunku atsekti vykdymo kelią.
- Konteksto Perjungimas: Dažnas konteksto perjungimas tarp užduočių gali užgožti klaidų šaltinį.
- Klaidų Sklaida: Vienos korutinos klaidos gali būti iškart nepastebimos iškviečiančioje korutinoje, todėl sunku nustatyti pagrindinę priežastį.
- Lenktynių Sąlygos: Bendri ištekliai, kuriuos vienu metu pasiekia kelios korutinos, gali sukelti lenktynių sąlygas, dėl kurių elgesys tampa nenuspėjamas.
- Aklavietės: Korutinos, laukiančios viena kitos neribotą laiką, gali sukelti aklavietes, sustabdančias programą.
Asyncio Derinimo Režimo Pristatymas
asyncio
derinimo režimas suteikia vertingų įžvalgų apie jūsų asinchroninio kodo vykdymą. Jis siūlo šias funkcijas:
- Išsamus Registravimas: Registruoja įvairius įvykius, susijusius su korutinų kūrimu, vykdymu, atšaukimu ir išimčių apdorojimu.
- Įspėjimai apie Ištekliaus: Aptinka neuždarytus lizdus, neuždarytus failus ir kitus išteklių nutekėjimus.
- Lėto Atgalinio Iškvietimo Aptikimas: Nustato atgalinius iškvietimus, kurių vykdymas trunka ilgiau nei nurodyta riba, o tai rodo galimas našumo problemas.
- Užduočių Atšaukimo Stebėjimas: Pateikia informaciją apie užduoties atšaukimą, padeda suprasti, kodėl užduotys atšaukiamos ir ar jos tvarkomos tinkamai.
- Išimties Kontekstas: Suteikia daugiau konteksto išimtims, iškeltoms korutinose, todėl lengviau atsekti klaidą iki jos šaltinio.
Asyncio Derinimo Režimo Įjungimas
Galite įjungti asyncio
derinimo režimą keliais būdais:
1. Naudojant PYTHONASYNCIODEBUG
Aplinkos Kintamąjį
Paprasčiausias būdas įjungti derinimo režimą yra nustatyti PYTHONASYNCIODEBUG
aplinkos kintamąjį į 1
prieš paleidžiant Python scenarijų:
export PYTHONASYNCIODEBUG=1
python your_script.py
Tai įjungs derinimo režimą visam scenarijui.
2. Derinimo Vėliavėlės Nustatymas asyncio.run()
Jei naudojate asyncio.run()
, norėdami paleisti įvykių ciklą, galite perduoti argumentą debug=True
:
import asyncio
async def main():
print("Hello, asyncio!")
if __name__ == "__main__":
asyncio.run(main(), debug=True)
3. Naudojant loop.set_debug()
Taip pat galite įjungti derinimo režimą gaudami įvykių ciklo egzempliorių ir iškviesdami set_debug(True)
:
import asyncio
async def main():
print("Hello, asyncio!")
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.set_debug(True)
loop.run_until_complete(main())
Derinimo Išvesties Interpretavimas
Įjungus derinimo režimą, asyncio
generuos išsamius žurnalo pranešimus. Šie pranešimai suteikia vertingos informacijos apie jūsų korutinų vykdymą. Štai keletas dažniausių derinimo išvesties tipų ir kaip juos interpretuoti:
1. Korutinų Kūrimas ir Vykdymas
Derinimo režimas registruoja, kada kuriamos ir paleidžiamos korutinos. Tai padeda jums stebėti jūsų korutinų gyvavimo ciklą:
asyncio | execute () running at example.py:3>
asyncio | Task-1: created at example.py:7
Ši išvestis rodo, kad užduotis pavadinimu Task-1
buvo sukurta 7 eilutėje example.py
ir šiuo metu vykdo korutiną a()
, apibrėžtą 3 eilutėje.
2. Užduoties Atšaukimas
Kai užduotis atšaukiama, derinimo režimas registruoja atšaukimo įvykį ir atšaukimo priežastį:
asyncio | Task-1: cancelling
asyncio | Task-1: cancelled by () running at example.py:10>
Tai rodo, kad Task-1
buvo atšaukta Task-2
. Užduoties atšaukimo supratimas yra labai svarbus norint išvengti netikėto elgesio.
3. Įspėjimai apie Ištekliaus
Derinimo režimas įspėja apie neuždarytus išteklius, tokius kaip lizdai ir failai:
ResourceWarning: unclosed
Šie įspėjimai padeda nustatyti ir ištaisyti išteklių nutekėjimus, kurie gali pabloginti našumą ir nestabilizuoti sistemą.
4. Lėto Atgalinio Iškvietimo Aptikimas
Derinimo režimas gali aptikti atgalinius iškvietimus, kurių vykdymas trunka ilgiau nei nurodyta riba. Tai padeda nustatyti našumo problemas:
asyncio | Task was destroyed but it is pending!
pending time: 12345.678 ms
5. Išimčių Apdorojimas
Derinimo režimas suteikia daugiau konteksto išimtims, iškeltoms korutinose, įskaitant užduotį ir korutiną, kurioje įvyko išimtis:
asyncio | Task exception was never retrieved
future: () done, raised ValueError('Invalid value')>
Ši išvestis rodo, kad ValueError
buvo iškeltas Task-1
ir nebuvo tinkamai apdorotas.
Praktiniai Derinimo su Asyncio Derinimo Režimu Pavyzdžiai
Pažvelkime į keletą praktinių pavyzdžių, kaip naudoti asyncio
derinimo režimą dažnoms problemoms diagnozuoti:
1. Neuždarytų Lizdų Aptikimas
Apsvarstykite šį kodą, kuris sukuria lizdą, bet jo tinkamai neuždaro:
import asyncio
import socket
async def handle_client(reader, writer):
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
print(f"Received {message!r} from {addr!r}")
print(f"Send: {message!r}")
writer.write(data)
await writer.drain()
# Missing: writer.close()
async def main():
server = await asyncio.start_server(
handle_client,
'127.0.0.1',
8888
)
addr = server.sockets[0].getsockname()
print(f'Serving on {addr}')
async with server:
await server.serve_forever()
if __name__ == "__main__":
asyncio.run(main(), debug=True)
Kai paleidžiate šį kodą įjungę derinimo režimą, pamatysite ResourceWarning
, nurodantį neuždarytą lizdą:
ResourceWarning: unclosed
Norėdami tai ištaisyti, turite užtikrinti, kad lizdas būtų tinkamai uždarytas, pavyzdžiui, įtraukdami writer.close()
į handle_client
korutiną ir laukdami jo:
writer.close()
await writer.wait_closed()
2. Lėtų Atgalinių Iškvietimų Nustatymas
Tarkime, kad turite korutiną, kuri atlieka lėtą operaciją:
import asyncio
import time
async def slow_function():
print("Starting slow function")
time.sleep(2)
print("Slow function finished")
return "Result"
async def main():
task = asyncio.create_task(slow_function())
result = await task
print(f"Result: {result}")
if __name__ == "__main__":
asyncio.run(main(), debug=True)
Nors numatytoji derinimo išvestis tiesiogiai nenurodo lėtų atgalinių iškvietimų, sujungus ją su atidžiu registravimu ir profiliavimo įrankiais (pvz., cProfile arba py-spy), galite susiaurinti lėtas kodo dalis. Apsvarstykite galimybę registruoti laiko žymes prieš ir po potencialiai lėtų operacijų. Tada įrankiai, tokie kaip cProfile, gali būti naudojami registruotų funkcijų iškvietimų profiliavimui, norint izoliuoti problemas.
3. Užduoties Atšaukimo Derinimas
Apsvarstykite scenarijų, kai užduotis netikėtai atšaukiama:
import asyncio
async def worker():
try:
while True:
print("Working...")
await asyncio.sleep(0.5)
except asyncio.CancelledError:
print("Worker cancelled")
async def main():
task = asyncio.create_task(worker())
await asyncio.sleep(2)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("Task cancelled in main")
if __name__ == "__main__":
asyncio.run(main(), debug=True)
Derinimo išvestis parodys atšauktą užduotį:
asyncio | execute started at example.py:16>
Working...
Working...
Working...
Working...
asyncio | Task-1: cancelling
Worker cancelled
asyncio | Task-1: cancelled by result=None>
Task cancelled in main
Tai patvirtina, kad užduotį atšaukė main()
korutina. except asyncio.CancelledError
blokas leidžia atlikti valymą prieš visiškai nutraukiant užduotį, užkertant kelią išteklių nutekėjimui ar nenuosekliai būsenai.
4. Išimčių Tvarkymas Korutinose
Tinkamas išimčių tvarkymas yra labai svarbus asinchroniniame kode. Apsvarstykite šį pavyzdį su neapdorota išimtimi:
import asyncio
async def divide(x, y):
return x / y
async def main():
result = await divide(10, 0)
print(f"Result: {result}")
if __name__ == "__main__":
asyncio.run(main(), debug=True)
Derinimo režimas praneš apie neapdorotą išimtį:
asyncio | Task exception was never retrieved
future: result=None, exception=ZeroDivisionError('division by zero')>
Norėdami apdoroti šią išimtį, galite naudoti try...except
bloką:
import asyncio
async def divide(x, y):
return x / y
async def main():
try:
result = await divide(10, 0)
print(f"Result: {result}")
except ZeroDivisionError as e:
print(f"Error: {e}")
if __name__ == "__main__":
asyncio.run(main(), debug=True)
Dabar išimtis bus sugauta ir apdorota tinkamai.
Geriausia Asyncio Derinimo Praktika
Štai keletas geriausių asyncio
kodo derinimo praktikų:
- Įjunkite Derinimo Režimą: Visada įjunkite derinimo režimą kūrimo ir testavimo metu.
- Naudokite Registravimą: Įtraukite išsamų registravimą į savo korutinas, kad galėtumėte stebėti jų vykdymo srautą. Naudokite
logging.getLogger('asyncio')
konkretiems asyncio įvykiams ir savo registratorius programos specifiniams duomenims. - Apdorokite Išimtis: Įdiekite tvirtą išimčių tvarkymą, kad išvengtumėte neapdorotų išimčių, kurios sugadintų jūsų programą.
- Naudokite Užduočių Grupes (Python 3.11+): Užduočių grupės supaprastina išimčių tvarkymą ir atšaukimą susijusių užduočių grupėse.
- Profiluokite Savo Kodą: Naudokite profiliavimo įrankius, kad nustatytumėte našumo problemas.
- Parašykite Vienetinius Testus: Parašykite išsamius vienetinius testus, kad patikrintumėte savo korutinų elgesį.
- Naudokite Tipo Užrašus: Pasinaudokite tipo užrašais, kad anksčiau pastebėtumėte su tipais susijusias klaidas.
- Apsvarstykite galimybę naudoti derintuvą: Tokie įrankiai kaip `pdb` arba IDE derintuvai gali būti naudojami norint žingsnis po žingsnio pereiti per asyncio kodą. Tačiau jie dažnai būna mažiau veiksmingi nei derinimo režimas su atidžiu registravimu dėl asinchroninio vykdymo pobūdžio.
Pažangios Derinimo Technikos
Be pagrindinio derinimo režimo, apsvarstykite šias pažangias technikas:
1. Individualios Įvykių Ciklo Politikos
Galite sukurti individualias įvykių ciklo politikas, kad perimtumėte ir registruotumėte įvykius. Tai leidžia jums dar labiau kontroliuoti derinimo procesą.
2. Trečiųjų Šalių Derinimo Įrankių Naudojimas
Keli trečiųjų šalių derinimo įrankiai gali padėti jums derinti asyncio
kodą, pvz.:
- PySnooper: Galingas derinimo įrankis, kuris automatiškai registruoja jūsų kodo vykdymą.
- pdb++: Patobulinta standartinio
pdb
derintuvo versija su patobulintomis funkcijomis. - asyncio_inspector: Biblioteka, specialiai sukurta asyncio įvykių ciklams tikrinti.
3. Monkey Patching (Naudokite Atsargiai)
Esant ekstremalioms situacijoms, galite naudoti monkey patching, kad pakeistumėte asyncio
funkcijų elgesį derinimo tikslais. Tačiau tai turėtų būti daroma atsargiai, nes tai gali sukelti subtilių klaidų ir apsunkinti jūsų kodo priežiūrą. Tai paprastai nerekomenduojama, nebent tai visiškai būtina.
Išvada
Derinti asinchroninį kodą gali būti sudėtinga, tačiau asyncio
derinimo režimas suteikia vertingų įrankių ir įžvalgų, kad supaprastintų procesą. Įjungę derinimo režimą, interpretuodami išvestį ir laikydamiesi geriausios praktikos, galite efektyviai nustatyti ir išspręsti dažnas problemas savo asinchroninėse programose, o tai leis jums sukurti tvirtesnį ir našesnį kodą. Nepamirškite sujungti derinimo režimą su registravimu, profiliavimu ir kruopščiu testavimu, kad pasiektumėte geriausių rezultatų. Su praktika ir tinkamais įrankiais galite įvaldyti asyncio
korutinų derinimo meną ir sukurti keičiamo dydžio, efektyvias ir patikimas asinchronines programas.