5

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.

Sociopath
  • 13,068
  • 19
  • 47
  • 75
  • 4
    it sounds like a *fire-and-forget* approach. So, if you are interested in `asyncio` this question may help: https://stackoverflow.com/q/37278647/4636715 – vahdet Dec 06 '19 at 12:46
  • Try using celery http://www.celeryproject.org,In celery you can send your function to queue and continue with next line of code. – krishna Feb 29 '20 at 17:31

7 Answers7

5

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.

Chen A.
  • 10,140
  • 3
  • 42
  • 61
1

Maybe working with subproccesses is what you need? You can try something like:

import subprocess

subprocess.call(func1())
JaFizz
  • 328
  • 2
  • 20
0

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
An0ther0ne
  • 324
  • 3
  • 8
  • My requirement is such that I don;'t want to wait for `func1` to finish it's execution. Above code snippet is just an example. In practice `func1` may take hours to execute – Sociopath Dec 06 '19 at 13:35
0

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
0

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})

Gabriel Cappelli
  • 3,632
  • 1
  • 17
  • 31
0

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

SajanGohil
  • 960
  • 13
  • 26
0

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!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459