You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django使用threading启动循环任务仍阻塞HTTP请求的原因查询

问题描述

以下是价格监控模块的代码:

import threading
import time
import pandas as pd

from binance.client import Client
import json

from api_create_alert.models import Alerts
from service_get_data.get_api_secret import get_secret

alerts = Alerts.objects.all().values()
df = pd.DataFrame(alerts)

data = get_secret()
data = json.loads(data)
client = Client(data['BINANCE_PUBLIC_API'], data['BINANCE_SECRET_API'])

def price_watch():
    interval = 10

    while True:
        coins = client.get_all_tickers()

        for coin in coins:
            symbol = coin['symbol']
            price = float(coin['price'])
            rows = df[((df.symbol == symbol) & (df.direction == 'ABOVE') & (price > df.price)) | ((df.symbol == symbol) & (df.direction == 'BELOW') & (price < df.price))]

            if len(rows) > 0:
                print(rows)

        time.sleep(interval)

thread = threading.Thread(target=price_watch)
thread.start()

我在apps.py中按如下方式导入该模块,在服务启动时运行任务:

def ready(self):
    if os.environ.get("RUN_MAIN") == "true":
        import service_price_watch.price_watch

该任务是每10秒执行一次的循环,但实际运行时每次循环启动,Django应用都会被阻塞,无法响应任何HTTP请求,直到循环执行完毕才恢复。我已经使用了threading模块启动任务,该模块的作用不就是实现异步操作吗?请问为什么还会出现阻塞请求的情况?


原因分析
  • Python的线程受全局解释器锁(GIL)限制,同一时间只有一个线程能执行Python字节码。你代码中每次循环要遍历币安返回的上千个交易对,还要执行pandas全局筛选,属于CPU密集型操作,会长时间占用GIL,导致处理HTTP请求的主线程拿不到执行权限,出现阻塞。
  • Django默认的开发服务器(runserver)默认是单线程运行模式,所有请求和后台线程共享同一个进程的CPU资源,进一步放大了阻塞效应。
  • 你创建的线程没有设置为守护线程,虽然不是直接导致阻塞的原因,但会出现Django进程退出后子线程仍残留的问题。
  • 额外的隐藏问题:你的告警规则DataFrame是在模块导入时一次性查询生成的,后续用户新增、修改、删除告警规则都不会同步到内存中的df,监控逻辑永远使用服务启动时的旧规则。

解决方案

1. 修复线程配置

创建线程时添加daemon=True参数,确保主进程退出时子线程同步销毁:

thread = threading.Thread(target=price_watch, daemon=True)
thread.start()

2. 启用Django开发服务器多线程模式

启动服务时添加--threaded参数,让主线程可以独立处理HTTP请求:

python manage.py runserver --threaded

3. 优化监控逻辑降低CPU占用

把告警规则按交易对分组存储,避免每次循环全局遍历pandas表,同时每次循环刷新告警规则:

def price_watch():
    interval = 10
    while True:
        # 每次循环刷新最新告警规则
        alerts = Alerts.objects.all().values()
        df = pd.DataFrame(alerts)
        # 按交易对分组生成规则字典,减少后续筛选开销
        alert_map = df.groupby('symbol').apply(lambda x: x.to_dict('records')).to_dict()
        
        coins = client.get_all_tickers()
        for coin in coins:
            symbol = coin['symbol']
            price = float(coin['price'])
            # 仅处理有告警规则的交易对
            if symbol not in alert_map:
                continue
            for rule in alert_map[symbol]:
                if (rule['direction'] == 'ABOVE' and price > rule['price']) or (rule['direction'] == 'BELOW' and price < rule['price']):
                    print(f"触发告警:{symbol} 价格{price} 达到{rule['direction']} {rule['price']}")
        
        time.sleep(interval)

4. 生产环境替代方案

如果是线上部署,不要用内置threading做后台常驻任务,改用成熟的任务调度框架:

  • 定时任务场景可以用Celery Beat + Redis/RabbitMQ
  • 轻量场景可以用Django-Q、APScheduler集成到Django中

内容的提问来源于stack exchange,提问作者Kadiem Alqazzaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 14:09:00