Flask
Web Development
Background Threads
Python
Multithreading

flask application with background threads

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Background threads are a practical way to let a Flask request return quickly while a small amount of extra work continues in the same process. They are useful for lightweight tasks such as logging, cache refreshes, or short notifications, but they need careful boundaries because Flask request context, database state, and production worker models do not automatically behave the way many people expect.

Start with a Small, Explicit Threading Pattern

The basic pattern is simple: collect the values you need during the request, start a worker thread, and return the response immediately.

python
1from flask import Flask, jsonify
2from threading import Thread
3import time
4
5app = Flask(__name__)
6
7
8def send_notification(user_id: int) -> None:
9    time.sleep(2)
10    print(f"notification sent to user {user_id}")
11
12
13@app.route("/notify/<int:user_id>")
14def notify(user_id: int):
15    thread = Thread(target=send_notification, args=(user_id,), daemon=True)
16    thread.start()
17    return jsonify({"status": "queued", "user_id": user_id})

This works because the background function receives plain data, not Flask request objects. That distinction is important. The worker should get simple values such as IDs, filenames, or payload strings, not lazy references back into the request lifecycle.

Respect Flask Context Boundaries

Flask’s request, g, and current_app objects are context-local. They are available while a request is active, but a new thread does not automatically inherit that request context.

This is a common bug:

python
1from flask import request
2
3
4def background_task():
5    print(request.path)

That code may fail because the request context is gone by the time the thread tries to use it. The safe fix is to extract what you need before starting the thread:

python
1from flask import Flask, jsonify, request
2from threading import Thread
3
4app = Flask(__name__)
5
6
7def log_request(path: str, user_agent: str) -> None:
8    print(path, user_agent)
9
10
11@app.route("/example")
12def example():
13    path = request.path
14    user_agent = request.headers.get("User-Agent", "")
15    Thread(target=log_request, args=(path, user_agent), daemon=True).start()
16    return jsonify({"status": "accepted"})

If a worker really needs application-level configuration, create an application context inside the thread:

python
1from flask import Flask
2from threading import Thread
3
4app = Flask(__name__)
5app.config["GREETING"] = "hello"
6
7
8def worker(app: Flask) -> None:
9    with app.app_context():
10        print(app.config["GREETING"])
11
12
13Thread(target=worker, args=(app,), daemon=True).start()

That gives access to application resources, but it still does not recreate a request context.

Prefer a Thread Pool Over Unbounded Threads

Starting a raw thread for every request is workable at low volume, but it does not scale gracefully. A shared ThreadPoolExecutor is usually cleaner and places an upper bound on concurrency.

python
1from concurrent.futures import ThreadPoolExecutor
2from flask import Flask, jsonify
3import time
4
5app = Flask(__name__)
6executor = ThreadPoolExecutor(max_workers=4)
7
8
9def process_event(event_id: int) -> None:
10    time.sleep(1)
11    print(f"processed event {event_id}")
12
13
14@app.route("/events/<int:event_id>")
15def queue_event(event_id: int):
16    executor.submit(process_event, event_id)
17    return jsonify({"status": "submitted", "event_id": event_id})

This approach is easier to reason about under load because the process does not create an unlimited number of threads. It also gives you one place to coordinate shutdown if the app exits cleanly.

Know When Threads Stop Being the Right Tool

In-process threads are best for short, disposable work. They are a poor fit for jobs that must survive restarts, be retried, or run for a long time. If the Flask process crashes, queued thread work is simply gone.

That is why production systems typically move serious background work to a task queue such as Celery, RQ, or Dramatiq. Those tools give you durable job storage, separate workers, retry policies, and monitoring. A background thread in Flask gives you none of that.

Another deployment detail matters: a WSGI server may run multiple worker processes. Threads created in one process are invisible to the others. That is fine for small fire-and-forget tasks, but it means you should not mistake in-process threading for a global job system.

Common Pitfalls

The most common mistake is reading request or g inside the worker thread after the response has already returned. Always pass concrete data into the worker instead.

Another issue is launching threads for tasks that should be durable. If losing the job would be unacceptable, a web-process thread is the wrong place to run it.

Flask development mode can also confuse people because the reloader may start the application twice. If you initialize long-lived background infrastructure at import time, you may accidentally create duplicates during local development.

Finally, be careful with database connections or sessions. A worker thread should create or acquire its own database resources rather than reusing request-scoped state that belongs to the original handler.

Summary

  • Flask background threads are useful for short, lightweight work after a response is sent.
  • Pass plain data to the worker instead of relying on request-scoped globals.
  • Use app.app_context() only when the thread needs application-level resources.
  • Prefer a bounded executor over creating an unlimited thread per request.
  • For durable, retryable, or long-running jobs, use a real task queue instead of in-process threads.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.