2

I am using the Sanic as the server and try to handle multiple request concurrently.

I have used the await for the encode function(I use for loop to simulate do something) but when I try the time curl http://0.0.0.0:8000/ in two separate consoles, it doesn't run concurrently.

I have searched google but only find event_loop but it is to schedule registered conroutines.

How do I await the for loop so the requests won't be blocked?

Thank you.

from sanic import Sanic
from sanic import response
from signal import signal, SIGINT
import asyncio
import uvloop

app = Sanic(__name__)


@app.route("/")
async def test(request):
    # await asyncio.sleep(5)
    await encode()
    return response.json({"answer": "42"})

async def encode():
    print('encode')
    for i in range(0, 300000000):
        pass

asyncio.set_event_loop(uvloop.new_event_loop())
server = app.create_server(host="0.0.0.0", port=8000)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(server)
signal(SIGINT, lambda s, f: loop.stop())
try:
    loop.run_forever()
except:
    loop.stop()
Jason
  • 1,573
  • 3
  • 18
  • 46

2 Answers2

1

Running for i in range() is blocking. If you change that to put your await asyncio.sleep(5) into the encode method, you will see that it operates as expected.

@app.route("/")
async def test(request):
    await encode()
    return response.json({"answer": "42"})

async def encode():
    print('encode')
    await asyncio.sleep(5)

When you call await encode() and encode is a blocking method, then it still is going to block because you are not "awaiting" anything else. Your thread is still locked up.

You could also add another worker:

app.create_server(worker=2)

Try looking through this answer

Adam Hopkins
  • 6,837
  • 6
  • 32
  • 52
1

Since the async handler is actually running in an eventloop, it is running asynchronously as callback rather than concurrently. loop.run_forever() would call loop._run_once over and over again to run all the registered event, each await would stop the coroutine and yield control back to the eventloop and eventloop arrange to run the next event.

So basically if you don't want blocking in a long running for-loop, you need to manually hand over the control back to the eventloop inside the for-loop, see the issue about relinquishing control:

async def encode():
    print('encode')
    for i in range(0, 300000000):
        await asyncio.sleep(0)

Here is a quote from Guido:

asyncio.sleep(0) means just that -- let any other tasks run and then come back here.

patpat
  • 664
  • 1
  • 5
  • 14