最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Why CPU bound task doesn't block asyncio event loop? - Stack Overflow

programmeradmin0浏览0评论

I want to go deep in asynchronous task execution in python and I made experiment when CPU Bound task runs with IO bound task simultaneously.

I suppose that CPU Bound task blocks the event loop and IO Bound task will start after CPU Bound task finished. But in my experiment I see that IO Bound task start and finishes simultaneously with CPU Bound. I want to find answer, why it happens.

To make IO bound load I use asyncio.sleep, to make CPU bound load I use for loop with multiplying and division.

Experiment code:

import asyncio
from datetime import datetime

async def io_bound(i):
    print(f'start io_bound {i} {datetime.now().strftime("%H:%M:%S")}')
    await asyncio.sleep(1)
    print(f'finish io_bound {i} {datetime.now().strftime("%H:%M:%S")}')

async def cpu_bound():
    print(f'start cpu_bound {datetime.now().strftime("%H:%M:%S")}')
    for i in range(5 * 10**7):
        i *= i+1
        i /= i+1
    print(f'finish cpu_bound {datetime.now().strftime("%H:%M:%S")}')

async def main():
    await io_bound(1)
    asyncio.create_task(cpu_bound())
    await io_bound(2)

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())

output:

start io_bound 1 17:28:20
finish io_bound 1 17:28:21
start io_bound 2 17:28:21
start cpu_bound 17:28:21
finish cpu_bound 17:28:26
finish io_bound 2 17:28:26

I expected that output should be:

start io_bound 1 17:28:20
finish io_bound 1 17:28:21
start io_bound 2 17:28:21
start cpu_bound 17:28:21
finish cpu_bound 17:28:26
finish io_bound 2 17:28:27

That is, io_bound task should finish one second after cpu_bound is finished.

So the question is: Why io_bound finishes immediately after cpu_bound is finished? Doesn't CPU bound task block execution of other tasks?

Thanks for any responses.

I want to go deep in asynchronous task execution in python and I made experiment when CPU Bound task runs with IO bound task simultaneously.

I suppose that CPU Bound task blocks the event loop and IO Bound task will start after CPU Bound task finished. But in my experiment I see that IO Bound task start and finishes simultaneously with CPU Bound. I want to find answer, why it happens.

To make IO bound load I use asyncio.sleep, to make CPU bound load I use for loop with multiplying and division.

Experiment code:

import asyncio
from datetime import datetime

async def io_bound(i):
    print(f'start io_bound {i} {datetime.now().strftime("%H:%M:%S")}')
    await asyncio.sleep(1)
    print(f'finish io_bound {i} {datetime.now().strftime("%H:%M:%S")}')

async def cpu_bound():
    print(f'start cpu_bound {datetime.now().strftime("%H:%M:%S")}')
    for i in range(5 * 10**7):
        i *= i+1
        i /= i+1
    print(f'finish cpu_bound {datetime.now().strftime("%H:%M:%S")}')

async def main():
    await io_bound(1)
    asyncio.create_task(cpu_bound())
    await io_bound(2)

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())

output:

start io_bound 1 17:28:20
finish io_bound 1 17:28:21
start io_bound 2 17:28:21
start cpu_bound 17:28:21
finish cpu_bound 17:28:26
finish io_bound 2 17:28:26

I expected that output should be:

start io_bound 1 17:28:20
finish io_bound 1 17:28:21
start io_bound 2 17:28:21
start cpu_bound 17:28:21
finish cpu_bound 17:28:26
finish io_bound 2 17:28:27

That is, io_bound task should finish one second after cpu_bound is finished.

So the question is: Why io_bound finishes immediately after cpu_bound is finished? Doesn't CPU bound task block execution of other tasks?

Thanks for any responses.

Share Improve this question edited yesterday VPfB 17.3k7 gold badges50 silver badges90 bronze badges asked 2 days ago PhilippPhilipp 271 silver badge6 bronze badges 2
  • 1 To make it more easy, you can use time.sleep as a CPU bound dummy. Makes it more easy to control/replicate results – CutePoison Commented 2 days ago
  • @CutePoison, yep, I started with "classic" time.sleep, but thought that it might affects on execution. So I made more real CPU bond load. – Philipp Commented 2 days ago
Add a comment  | 

3 Answers 3

Reset to default 1

The asyncio.sleep(1) tells the asyncio scheduler to stop running the current task now and to schedule to run it again one second from now. This inserts a TimerHandle object (a low-level timed callback) to the queue of waiting TimerHandles.

The queue is sorted by the time of desired action. In order to be able to sort it, the time must be absolute. The "one second from now" is also stored as an absolute time.

The event loop is then blocked by a CPU bound task for few seconds. When the loop gains control again, the scheduled TimerHandle is already too late, so it is run immediately.

You could try asyncio.sleep(10) to verify that it means continue 10 seconds from now.

await asyncio.sleep(1) means "suspend this task until 1 second has passed". It doesn't need to be 1 second of the event loop doing nothing - time that passes while other tasks are executing counts.

By the time cpu_bound() has finished, the sleep duration is over, so io_bound(2) can finish immediately. io_bound(2) doesn't need to spend 1 second hogging the event loop before it finishes.

It does block the event loop. That's why the second io_bound takes 5 seconds to be resumed; the event loop can't do anything while cpu_bound is running. But once cpu_bound finishes, the event loop looks and says "hey, I've got a one-second timer that I set five seconds ago, that's overdue!" and jumps back into io_bound. There's no reason for it to wait an additional second.

发布评论

评论列表(0)

  1. 暂无评论