Masalah alur eksekusi Asyncio

saya sedikit baru mengenal asyncio dengan python. Saya mencoba menjalankan kode sederhana ini tetapi saya tidak tahu mengapa saya mendapatkan hasil yang tidak terduga ini.

Apa yang saya lakukan adalah, dalam fungsi outer, saya membuat tugas async dan menyimpannya dalam array tasks. Sebelum menunggu tugas ini saya menulis pernyataan cetak print("outer") yang harus dijalankan di setiap iterasi. Dan di dalam tugas saya menulis pernyataan cetak lainnya print("inner") dalam fungsi inner. Tetapi entah bagaimana saya mendapatkan hasil yang tidak terduga.

Ini kodenya -

import asyncio


def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(outer(loop))
    loop.close()


async def outer(loop):
    tasks = []
    for i in range(0, 5):
        tasks.append(loop.create_task(inner()))

    for task in tasks:
        print("outer")
        await task


async def inner():
    print("inner")
    await asyncio.sleep(0.5)

if __name__ == '__main__':
    main()

Inilah hasilnya -

outer
inner
inner
inner
inner
inner
outer
outer
outer
outer

Hasil yang saya harapkan adalah -

outer
inner
outer
inner
outer
inner
outer
inner
outer
inner

Mengapa semua inner mencetak sebelum outer. Apa alur eksekusi asyncio yang benar. Terima kasih sebelumnya.


person shikhar srivastava    schedule 29.03.2020    source sumber
comment
Setiap tugas/coroutine berjalan hingga pernyataan await kemudian ditangguhkan.. Contoh Anda mirip dengan contoh coroutine di dokumen yang memiliki hasil serupa.   -  person wwii    schedule 29.03.2020
comment
Dari dokumen - While a Task is running in the event loop, no other Tasks can run in the same thread. When a Task executes an await expression, the running Task gets suspended, and the event loop executes the next Task.   -  person wwii    schedule 29.03.2020
comment
Namun ketika satu tugas ditunggu, maka untuk memulai tugas baru, perulangan peristiwa harus menjalankan iterasi perulangan for berikutnya. Jika benar, maka outer seharusnya dicetak.   -  person shikhar srivastava    schedule 30.03.2020


Jawaban (2)


In async def outer(loop)

for i in range(0, 5):
    tasks.append(loop.create_task(inner()))
  • Lima tugas inner baru dibuat, dijadwalkan, dan mulai dijalankan.
  • loop acara menjalankan tugas yang dijadwalkan hingga selesai.

Jika Anda menambahkan sedikit lagi ke inner dan outer ini akan menunjukkan proses ini dengan lebih baik:

async def outer(loop):
    tasks = []
    for i in range(0, 5):
        tasks.append(loop.create_task(inner(i)))
    await asyncio.sleep(3)
    for task in tasks:
        print('outer')
        await task

async def inner(n):
    print(f"inner {n} start")
    await asyncio.sleep(0.5)
    print(f'inner {n} end')
  • saat outer sedang tidur

    • The first task runs to its await statement
    • Perulangan peristiwa menangguhkan eksekusi tugas pertama
    • Perulangan peristiwa menjalankan tugas terjadwal berikutnya hingga pernyataan menunggunya
    • Hal ini berlanjut hingga setiap tugas mempunyai kesempatan untuk menjalankan pernyataan menunggunya, kemudian perulangan peristiwa mulai melihat sekeliling untuk melihat apakah ada tugas yang selesai, menunggu apa pun yang mereka tunggu - jika selesai, tugas tersebut akan dijalankan lagi.
    • Ini berlanjut sampai semua tugas selesai
  • Anda dapat melihat bahwa kelima tugas dijalankan dan diselesaikan sebelum perulangan for kedua dimulai.

  • pada perulangan for kedua, setiap tugas telah selesai sehingga await task tidak perlu menunggu apa pun dan outer dicetak lima kali berturut-turut.

Saya agak kabur tentang loop peristiwa yang mengendalikan semuanya - Saya belum menemukan dokumentasi eksplisit untuk itu - mungkin disinggung di create_task dokumen: Wrap the coro coroutine into a Task and schedule its execution. Saat Anda membuat tugas, tugas itu akan dijadwalkan. Saya telah melihat video di pyvideo.org yang menunjukkan proses itu, sayangnya saya tidak dapat segera menemukan video yang ingin saya tautkan.

person wwii    schedule 30.03.2020
comment
Apakah Anda menemukan tautan pyvideo.org yang menjelaskan proses lengkapnya? - person shikhar srivastava; 19.02.2021
comment
@shikharsrivastava itu luput dari perhatian saya. Ada terlalu banyak untuk ditonton/ditonton ulang untuk menemukannya. Saya bahkan mungkin salah mengingat bagian-bagian dari banyak hal – mengira mereka adalah satu. - person wwii; 19.02.2021

loop.create_task(inner()) segera mengantri semua 5 inner tugas untuk dijalankan, bukan await task. await task menangguhkan outer hingga tugas pertama sepenuhnya selesai, yaitu setidaknya 0,5 detik. Selama ini, semua inner tugas telah dijalankan satu kali hingga await, termasuk print.


async def outer(loop):
    tasks = []
    for i in range(0, 5):
        # queues task immediately
        tasks.append(loop.create_task(inner()))

    for task in tasks:
        print("outer")
        # suspends for at least 0.5 seconds
        await task


async def inner():
    # runs immediately
    print("inner")
    await asyncio.sleep(0.5)
person MisterMiyagi    schedule 30.03.2020
comment
aha!! Jika Anda menambahkan await asyncio.sleep(10); print('outer done sleeping' tepat setelah perulangan yang membuat tugas, Anda dapat melihat bahwa tugas tersebut dijalankan sebelum ada tugas yang ditunggu. terima kasih - person wwii; 30.03.2020