Threading not working in flask - python

I'm trying to use threading in my flask app, like:
#app.route('/index')
def index():
t = threading.Thread(do_sth_else())
t.start()
print('ready to response')
return render_template('index.html')
def do_sth_else():
time.sleep(5)
print('sth else done')
When calling 127.0.0.1:5000/index in the browser, the result in the server console is not what I expected:
sth else done
ready to response
I want the do_sth_else() function to run in some other thread, while the index() function go on returning the response right away, which means I should see the above result in defferent order.
So I want to know:
Why the index() function kept waiting until do_sth_else() is finished
How do I get the app working as I wanted
Thanks!

t = threading.Thread(do_sth_else()) calls do_sth_else() and pass it's result to Thread.
You should use it like t = threading.Thread(do_sth_else).

This example working as you want (tested on Python 3.4.3)
from time import sleep
from concurrent.futures import ThreadPoolExecutor
# DOCS https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor
executor = ThreadPoolExecutor(2)
#app.route('/index')
def index():
executor.submit(do_sth_else)
print('ready to response')
return render_template('index.html')
def do_sth_else():
print("Task started!")
sleep(10)
print("Task is done!")

For actual parallelization in Python, you should use the multiprocessing module to fork multiple processes that execute in parallel.
Python threads provide interleaving, but are in fact executed serially, not in parallel.
This applies to CPython due to the existence of global interpreter lock, otherwise true concurrency is bound to number of cpu's you have

Related

Python threading in flask webapp

I've been trying threads recently in my webapp, and I've come across an issue that I cannot seem to solve.
The issue is that I have an index page, and every time the user enters that page, a new thread is being started which checks for changes in my database in a while loop, although I only want one thread to be on at that moment. Is there a way to "kill" a thread when the index page is accessed the second time and then start a new one?
I did try to use is_alive() to check if a thread is already running, but had no success since all the time they're different.
Code example below:
#app.route("/")
def index():
#copy_current_request_context
def check_for_updates():
while True:
...... # Query for information
if something_changed:
socketio.emit('new_notifications', {'data': new_data})
if index_opened_again:
break
sleep(5)
index_thread = threading.Thread(target=check_for_updates, daemon=True)
index_thread.start()
return render_template("index.html")
I user the below code to kill threads when I am existing a server ,
My suggestion is to kill all active threadss before opening a new one
for id, thread in threading._active.items():
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, ctypes.py_object(SystemExit))
In my example I use a global variable in combination with a lock. It's certainly not optimal, but you can check if a thread is already running.
If you're using flask-socketio anyway, I recommend you take a look at start_background_task. The functionality is compatible with that of a standard thread.
from threading import Lock
from flask import Flask, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.secret_key = 'your secret here'
sio = SocketIO(app)
thread = None
thread_lock = Lock()
def background_task():
while True:
# ...
sleep(5)
#app.route('/')
def index():
global thread
with thread_lock:
if thread is None:
thread = sio.start_background_task(background_task)
return render_template('index.html')
if __name__ == '__main__':
sio.run(app)

Make function not to wait for other function inside it

I have a flask service as below:
from flask import Flask, request
import json
import time
app = Flask(__name__)
#app.route("/first", methods=["POST"])
def main():
print("Request received")
func1()
return json.dumps({"status": True})
def func1():
time.sleep(100)
print("Print function executed")
if __name__ == "__main__":
app.run("0.0.0.0", 8080)
So now when I make a request using http://localhost:8080/first
control goes to main method and it prints Request received and wait for func1 to get executed and then it returns {"status": True}
But now I don't want to wait for func1 to finish its execution instead it will sent {"status": True} and func1 will continue it's execution.
In order to reply to request from flask, you need the decorated function to finish (in your case, that's main).
If you want to execute something in parallel, you need to execute it in another thread or a process. Multi-process apps are used to achieve more than a single CPU. (CPU bound); in your case, you just need it to execute in parallel so it is better to go with threads.
A simple technique is to use ThreadPool. import ThreadPoolExecutor from concurrent.futures, then submit work to it, which allows your function execution code to continue. Try this:
from flask import Flask, request
import json
import time
import os
from concurrent.futures import ThreadPoolExecutor
app = Flask(__name__)
# Task manager executor
_threadpool_cpus = int(os.cpu_count() / 2)
EXECUTOR = ThreadPoolExecutor(max_workers=max(_threadpool_cpus, 2))
#app.route("/first", methods=["POST"])
def main():
print("Request received")
EXECUTOR.submit(func1)
return json.dumps({"status": True})
def func1():
time.sleep(2)
print("Print function executed")
if __name__ == "__main__":
app.run("0.0.0.0", 8080)
This will run the func1 in a different thread, allowing flask to respond the user without blocking until func1 is done.
Maybe working with subproccesses is what you need?
You can try something like:
import subprocess
subprocess.call(func1())
I think the problem is in the POST method, that you prescribed. Also 100 seconds sleep time too long :)
def func1():
print("Print function executed1")
time.sleep(10)
print("Print function executed2")
app = Flask(__name__)
#app.route("/first")
def main():
print("Request received1")
func1()
print("Request received2")
return json.dumps({"status": True})
if __name__ == "__main__":
app.run("0.0.0.0", 8080)
Output:
Request received1
Print function executed1
Print function executed2
Request received2
After receiving/executing a request for function 1, you can set/reset a global status flag/variable(e.g. flag_func_1 = True:Request Received ; False:Request Executed).
You can monitor the value of the flag_func_1 and can return {"status": True}immediately after setting flag.
Ex: inside main function you can do something like :
if(flag_func_1 == True):
func_1()
flag_func1 = False
Warning, this is not a robust solution. You should look into distributed queues to persist these requests (for example: RabbitMQ, Kafka, Redis)
That being said... You can use a thread to start the function.
from threading import Thread
#app.route("/first", methods=["GET"])
def main():
print("Request received")
Thread(target=func1, args=()).start()
return json.dumps({"status": True})
If you need flask to return a response before starting your func1(), you should checkout this answer which provides a details about necessary workings of flask.
Otherwise, you can use threading or multiprocessing:
from threading import Thread
from multiprocessing import Process #and multiprocessing queue if you use this
import queue #for passing messages between main and func1
message_queue = queue.Queue()
#app.route("/first", methods=["GET"])
def main():
print("Request received")
func_thread = Thread(target=func1, args=(), daemon=True).start() #daemon if it needs to die with main program, otherwise daemon=False
#or func_process = Process(...) #in case
return json.dumps({"status": True})
def func1():
...
print("func 1 ")
message_queue.put(...) #if you need to pass something
message_queue.get(...) #to get something like stopping signal
return
I think the simplest way to do what you're asking is to use the library, multiprocessing.
def run_together(*functions):
processes = []
for function in functions:
process = Process(target=function)
process.start()
processes.append(process)
for process in processes:
process.join()
#app.route("/first", methods=["POST"])
def main():
print("Request received")
return run_together(func1, func2)
def func1():
time.sleep(100)
print("Print function executed")
def func2():
return json.dumps({"status": True})
I wrote a rough code, I haven't tested it yet. But I hope it helps, cheerio!

Why I am not able to do simultaneous requests in Tornado?

Below tornado APP has 2 end points. One(/) is slow because it waits for an IO operation and other(/hello) is fast.
My requirement is to make a request to both end points simultaneously.I observed it takes 2nd request only after it finishes the 1st one. Even though It is asynchronous why it is not able to handle both requests at same time ?
How to make it to handle simultaneously?
Edit : I am using windows 7, Eclipse IDE
****************Module*****************
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
#tornado.web.asynchronous
def get(self):
self.do_something()
self.write("FINISHED")
self.finish()
def do_something(self):
inp = input("enter to continue")
print (inp)
class HelloHandler(tornado.web.RequestHandler):
def get(self):
print ("say hello")
self.write("Hello bro")
self.finish(
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
(r"/hello", HelloHandler)
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
It is asynchronous only if you make it so. A Tornado server runs in a single thread. If that thread is blocked by a synchronous function call, nothing else can happen on that thread in the meantime. What #tornado.web.asynchronous enables is the use of generators:
#tornado.web.asynchronous
def get(self):
yield from self.do_something()
^^^^^^^^^^
This yield/yield from (in current Python versions await) feature suspends the function and lets other code run on the same thread while the asynchronous call completes elsewhere (e.g. waiting for data from the database, waiting for a network request to return a response). I.e., if Python doesn't actively have to do something but is waiting for external processes to complete, it can yield processing power to other tasks. But since your function is very much running in the foreground and blocking the thread, nothing else will happen.
See http://www.tornadoweb.org/en/stable/guide/async.html and https://docs.python.org/3/library/asyncio.html.

Make a Python asyncio call from a Flask route

I want to execute an async function every time the Flask route is executed. Why is the abar function never executed?
import asyncio
from flask import Flask
async def abar(a):
print(a)
loop = asyncio.get_event_loop()
app = Flask(__name__)
#app.route("/")
def notify():
asyncio.ensure_future(abar("abar"), loop=loop)
return "OK"
if __name__ == "__main__":
app.run(debug=False, use_reloader=False)
loop.run_forever()
I also tried putting the blocking call in a separate thread. But it is still not calling the abar function.
import asyncio
from threading import Thread
from flask import Flask
async def abar(a):
print(a)
app = Flask(__name__)
def start_worker(loop):
asyncio.set_event_loop(loop)
try:
loop.run_forever()
finally:
loop.close()
worker_loop = asyncio.new_event_loop()
worker = Thread(target=start_worker, args=(worker_loop,))
#app.route("/")
def notify():
asyncio.ensure_future(abar("abar"), loop=worker_loop)
return "OK"
if __name__ == "__main__":
worker.start()
app.run(debug=False, use_reloader=False)
You can incorporate some async functionality into Flask apps without having to completely convert them to asyncio.
import asyncio
from flask import Flask
async def abar(a):
print(a)
loop = asyncio.get_event_loop()
app = Flask(__name__)
#app.route("/")
def notify():
loop.run_until_complete(abar("abar"))
return "OK"
if __name__ == "__main__":
app.run(debug=False, use_reloader=False)
This will block the Flask response until the async function returns, but it still allows you to do some clever things. I've used this pattern to perform many external requests in parallel using aiohttp, and then when they are complete, I'm back into traditional flask for data processing and template rendering.
import aiohttp
import asyncio
import async_timeout
from flask import Flask
loop = asyncio.get_event_loop()
app = Flask(__name__)
async def fetch(url):
async with aiohttp.ClientSession() as session, async_timeout.timeout(10):
async with session.get(url) as response:
return await response.text()
def fight(responses):
return "Why can't we all just get along?"
#app.route("/")
def index():
# perform multiple async requests concurrently
responses = loop.run_until_complete(asyncio.gather(
fetch("https://google.com/"),
fetch("https://bing.com/"),
fetch("https://duckduckgo.com"),
fetch("http://www.dogpile.com"),
))
# do something with the results
return fight(responses)
if __name__ == "__main__":
app.run(debug=False, use_reloader=False)
A simpler solution to your problem (in my biased view) is to switch to Quart from Flask. If so your snippet simplifies to,
import asyncio
from quart import Quart
async def abar(a):
print(a)
app = Quart(__name__)
#app.route("/")
async def notify():
await abar("abar")
return "OK"
if __name__ == "__main__":
app.run(debug=False)
As noted in the other answers the Flask app run is blocking, and does not interact with an asyncio loop. Quart on the other hand is the Flask API built on asyncio, so it should work how you expect.
Also as an update, Flask-Aiohttp is no longer maintained.
Your mistake is to try to run the asyncio event loop after calling app.run(). The latter doesn't return, it instead runs the Flask development server.
In fact, that's how most WSGI setups will work; either the main thread is going to busy dispatching requests, or the Flask server is imported as a module in a WSGI server, and you can't start an event loop here either.
You'll instead have to run your asyncio event loop in a separate thread, then run your coroutines in that separate thread via asyncio.run_coroutine_threadsafe(). See the Coroutines and Multithreading section in the documentation for what this entails.
Here is an implementation of a module that will run such an event loop thread, and gives you the utilities to schedule coroutines to be run in that loop:
import asyncio
import itertools
import threading
__all__ = ["EventLoopThread", "get_event_loop", "stop_event_loop", "run_coroutine"]
class EventLoopThread(threading.Thread):
loop = None
_count = itertools.count(0)
def __init__(self):
self.started = threading.Event()
name = f"{type(self).__name__}-{next(self._count)}"
super().__init__(name=name, daemon=True)
def __repr__(self):
loop, r, c, d = self.loop, False, True, False
if loop is not None:
r, c, d = loop.is_running(), loop.is_closed(), loop.get_debug()
return (
f"<{type(self).__name__} {self.name} id={self.ident} "
f"running={r} closed={c} debug={d}>"
)
def run(self):
self.loop = loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.call_later(0, self.started.set)
try:
loop.run_forever()
finally:
try:
shutdown_asyncgens = loop.shutdown_asyncgens()
except AttributeError:
pass
else:
loop.run_until_complete(shutdown_asyncgens)
try:
shutdown_executor = loop.shutdown_default_executor()
except AttributeError:
pass
else:
loop.run_until_complete(shutdown_executor)
asyncio.set_event_loop(None)
loop.close()
def stop(self):
loop, self.loop = self.loop, None
if loop is None:
return
loop.call_soon_threadsafe(loop.stop)
self.join()
_lock = threading.Lock()
_loop_thread = None
def get_event_loop():
global _loop_thread
if _loop_thread is None:
with _lock:
if _loop_thread is None:
_loop_thread = EventLoopThread()
_loop_thread.start()
# give the thread up to a second to produce a loop
_loop_thread.started.wait(1)
return _loop_thread.loop
def stop_event_loop():
global _loop_thread
with _lock:
if _loop_thread is not None:
_loop_thread.stop()
_loop_thread = None
def run_coroutine(coro):
"""Run the coroutine in the event loop running in a separate thread
Returns a Future, call Future.result() to get the output
"""
return asyncio.run_coroutine_threadsafe(coro, get_event_loop())
You can use the run_coroutine() function defined here to schedule asyncio routines. Use the returned Future instance to control the coroutine:
Get the result with Future.result(). You can give this a timeout; if no result is produced within the timeout, the coroutine is automatically cancelled.
You can query the state of the coroutine with the .cancelled(), .running() and .done() methods.
You can add callbacks to the future, which will be called when the coroutine has completed, or is cancelled or raised an exception (take into account that this is probably going to be called from the event loop thread, not the thread that you called run_coroutine() in).
For your specific example, where abar() doesn't return any result, you can just ignore the returned future, like this:
#app.route("/")
def notify():
run_coroutine(abar("abar"))
return "OK"
Note that before Python 3.8 that you can't use an event loop running on a separate thread to create subprocesses with! See my answer to Python3 Flask asyncio subprocess in route hangs for backport of the Python 3.8 ThreadedChildWatcher class for a work-around for this.
For same reason you won't see this print:
if __name__ == "__main__":
app.run(debug=False, use_reloader=False)
print('Hey!')
loop.run_forever()
loop.run_forever() is never called since as #dirn already noted app.run is also blocking.
Running global blocking event loop - is only way you can run asyncio coroutines and tasks, but it's not compatible with running blocking Flask app (or with any other such thing in general).
If you want to use asynchronous web framework you should choose one created to be asynchronous. For example, probably most popular now is aiohttp:
from aiohttp import web
async def hello(request):
return web.Response(text="Hello, world")
if __name__ == "__main__":
app = web.Application()
app.router.add_get('/', hello)
web.run_app(app) # this runs asyncio event loop inside
Upd:
About your try to run event loop in background thread. I didn't investigate much, but it seems problem somehow related with tread-safety: many asyncio objects are not thread-safe. If you change your code this way, it'll work:
def _create_task():
asyncio.ensure_future(abar("abar"), loop=worker_loop)
#app.route("/")
def notify():
worker_loop.call_soon_threadsafe(_create_task)
return "OK"
But again, this is very bad idea. It's not only very inconvenient, but I guess wouldn't make much sense: if you're going to use thread to start asyncio, why don't just use threads in Flask instead of asyncio? You will have Flask you want and parallelization.
If I still didn't convince you, at least take a look at Flask-aiohttp project. It has close to Flask api and I think still better that what you're trying to do.
The main issue, as already explained in the other answers by #Martijn Pieters and #Mikhail Gerasimov is that app.run is blocking, so the line loop.run_forever() is never called. You will need to manually set up and maintain a run loop on a separate thread.
Fortunately, with Flask 2.0, you don't need to create, run, and manage your own event loop anymore. You can define your route as async def and directly await on coroutines from your route functions.
https://flask.palletsprojects.com/en/2.0.x/async-await/
Using async and await
New in version 2.0.
Routes, error handlers, before request, after request, and teardown
functions can all be coroutine functions if Flask is installed with
the async extra (pip install flask[async]). It requires Python 3.7+
where contextvars.ContextVar is available. This allows views to be
defined with async def and use await.
Flask will take care of creating the event loop on each request. All you have to do is define your coroutines and await on them to finish:
https://flask.palletsprojects.com/en/2.0.x/async-await/#performance
Performance
Async functions require an event loop to run. Flask, as a WSGI
application, uses one worker to handle one request/response cycle.
When a request comes into an async view, Flask will start an event
loop in a thread, run the view function there, then return the result.
Each request still ties up one worker, even for async views. The
upside is that you can run async code within a view, for example to
make multiple concurrent database queries, HTTP requests to an
external API, etc. However, the number of requests your application
can handle at one time will remain the same.
Tweaking the original example from the question:
import asyncio
from flask import Flask, jsonify
async def send_notif(x: int):
print(f"Called coro with {x}")
await asyncio.sleep(1)
return {"x": x}
app = Flask(__name__)
#app.route("/")
async def notify():
futures = [send_notif(x) for x in range(5)]
results = await asyncio.gather(*futures)
response = list(results)
return jsonify(response)
# The recommended way now is to use `flask run`.
# See: https://flask.palletsprojects.com/en/2.0.x/cli/
# if __name__ == "__main__":
# app.run(debug=False, use_reloader=False)
$ time curl -s -XGET 'http://localhost:5000'
[{"x":0},{"x":1},{"x":2},{"x":3},{"x":4}]
real 0m1.016s
user 0m0.005s
sys 0m0.006s
Most common recipes using asyncio can be applied the same way. The one thing to take note of is, as of Flask 2.0.1, we cannot use asyncio.create_task to spawn background tasks:
https://flask.palletsprojects.com/en/2.0.x/async-await/#background-tasks
Async functions will run in an event loop until they complete, at which
stage the event loop will stop. This means any additional spawned
tasks that haven’t completed when the async function completes will be
cancelled. Therefore you cannot spawn background tasks, for example
via asyncio.create_task.
If you wish to use background tasks it is best to use a task queue to
trigger background work, rather than spawn tasks in a view function.
Other than the limitation with create_task, it should work for use-cases where you want to make async database queries or multiple calls to external APIs.

How to use tornado's asynchttpclient alone?

I'm new to tornado.
What I want is to write some functions to fetch webpages asynchronously. Since no requesthandlers, apps, or servers involved here, I think I can use tornado.httpclient.AsyncHTTPClient alone.
But all the sample codes seem to be in a tornado server or requesthandler. When I tried to use it alone, it never works.
For example:
def handle(self,response):
print response
print response.body
#tornado.web.asynchronous
def fetch(self,url):
client=tornado.httpclient.AsyncHTTPClient()
client.fetch(url,self.handle)
fetch('http://www.baidu.com')
It says "'str' object has no attribute 'application'", but I'm trying to use it alone?
or :
#tornado.gen.coroutine
def fetch_with_coroutine(url):
client=tornado.httpclient.AsyncHTTPClient()
response=yield http_client.fetch(url)
print response
print response.body
raise gen.Return(response.body)
fetch_with_coroutine('http://www.baidu.com')
doesn't work either.
Earlier, I tried pass a callback to AsyncHTTPHandler.fetch, then start the IOLoop, It works and the webpage source code is printed. But I can't figure out what to do with the ioloop.
#tornado.web.asynchronous can only be applied to certain methods in RequestHandler subclasses; it is not appropriate for this usage.
Your second example is the correct structure, but you need to actually run the IOLoop. The best way to do this in a batch-style program is IOLoop.current().run_sync(fetch_with_coroutine). This starts the IOLoop, runs your callback, then stops the IOLoop. You should run a single function within run_sync(), and then use yield within that function to call any other coroutines.
For a more complete example, see https://github.com/tornadoweb/tornado/blob/master/demos/webspider/webspider.py
Here's an example I've used in the past...
from tornado.httpclient import AsyncHTTPClient
from tornado.ioloop import IOLoop
AsyncHTTPClient.configure(None, defaults=dict(user_agent="MyUserAgent"))
http_client = AsyncHTTPClient()
def handle_response(response):
if response.error:
print("Error: %s" % response.error)
else:
print(response.body)
async def get_content():
await http_client.fetch("https://www.integralist.co.uk/", handle_response)
async def main():
await get_content()
print("I won't wait for get_content to finish. I'll show immediately.")
if __name__ == "__main__":
io_loop = IOLoop.current()
io_loop.run_sync(main)
I've also detailed how to use Pipenv with tox.ini and Flake8 with this tornado example so others should be able to get up and running much more quickly https://gist.github.com/fd603239cacbb3d3d317950905b76096

Categories