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

如何在Django中运行无限循环且不中断逐次向模板展示循环结果

实现方案说明

你这个需求绝对不能在普通Django视图函数里直接写while True死循环,Django默认WSGI模式下每个请求会独占一个worker进程/线程,死循环会直接把worker占死,请求永远无法返回,并发稍高整个服务直接崩溃。正确的实现思路是把后台循环逻辑和前端结果展示完全解耦,下面给两个可直接落地的方案:


方案1:生产级通用方案(Celery + 结果存储 + SSE实时推送)

稳定性高,适合正式线上环境使用,不需要引入复杂的长连接组件:

  • 拆分循环逻辑为独立后台Celery任务
    把无限循环逻辑封装为Celery常驻任务,每跑完一轮就把轮次、结果、时间戳写入Redis这类多进程共享的存储中,同时在Redis里存一个任务运行标记位,需要手动终止时修改标记位,任务读到终止标记就自动退出循环。
    核心任务代码示例:
    # tasks.py
    from celery import shared_task
    import redis
    import time
    
    r = redis.Redis(host="127.0.0.1", port=6379, db=0)
    
    @shared_task
    def run_infinite_loop():
        round_num = 0
        # 初始化运行标记,避免重复启动任务
        if r.get("loop_task_status") == b"running":
            return
        r.set("loop_task_status", "running")
        while r.get("loop_task_status") == b"running":
            round_num += 1
            # 替换为你每轮需要执行的实际业务逻辑
            current_result = f"第{round_num}轮执行完成,业务返回值:模拟执行结果{round_num}"
            # 将结果存入Redis列表,可按需设置保留长度避免占满内存
            r.lpush("loop_result_list", f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {current_result}")
            r.ltrim("loop_result_list", 0, 1000)
            # 按需设置轮次间隔,避免占满CPU
            time.sleep(3)
    
  • 编写任务控制接口
    写两个简单的Django视图,一个用来触发启动循环任务,一个用来修改Redis标记位终止任务,记得加权限校验避免无关人员操作。
  • 实现前端实时结果展示
    用SSE(服务端推送事件)做实时结果推送,比WebSocket轻量,不需要双向通信的场景完全够用,不需要额外依赖:
    # views.py
    from django.http import StreamingHttpResponse
    import redis
    import json
    import time
    
    r = redis.Redis(host="127.0.0.1", port=6379, db=0)
    
    def sse_result_stream(request):
        def generate_events():
            last_count = r.llen("loop_result_list")
            while True:
                current_count = r.llen("loop_result_list")
                if current_count > last_count:
                    # 拉取新产生的结果推送给前端
                    new_results = r.lrange("loop_result_list", 0, current_count - last_count - 1)
                    for res in new_results:
                        yield f"data: {json.dumps({'content': res.decode('utf-8')}, ensure_ascii=False)}\n\n"
                    last_count = current_count
                time.sleep(0.5)
        response = StreamingHttpResponse(generate_events(), content_type="text/event-stream")
        response["Cache-Control"] = "no-cache"
        response["X-Accel-Buffering"] = "no" # 关闭Nginx反向代理缓冲,避免消息延迟
        return response
    
    前端模板只需要用原生EventSource连接SSE接口,收到消息就追加到页面即可,示例代码:
    <!-- loop_result.html -->
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>循环任务实时结果</title>
    </head>
    <body>
        <div>
            <button id="startBtn">启动循环</button>
            <button id="stopBtn">终止循环</button>
        </div>
        <div id="resultBox" style="width: 90%; height: 600px; margin: 20px auto; border: 1px solid #eee; padding: 15px; overflow-y: auto;"></div>
        <script>
            const resultBox = document.getElementById('resultBox');
            const es = new EventSource('/sse/stream/');
            es.onmessage = function(event) {
                const data = JSON.parse(event.data);
                const item = document.createElement('div');
                item.textContent = data.content;
                item.style.padding = '8px 0';
                item.style.borderBottom = '1px solid #f5f5f5';
                resultBox.prepend(item);
            }
            document.getElementById('startBtn').onclick = () => {
                fetch('/start/task/', {method:'POST', headers:{'X-CSRFToken':'{{ csrf_token }}'}})
            }
            document.getElementById('stopBtn').onclick = () => {
                fetch('/stop/task/', {method:'POST', headers:{'X-CSRFToken':'{{ csrf_token }}'}})
                .then(() => es.close())
            }
        </script>
    </body>
    </html>
    

方案2:轻量小项目方案(Django Channels + 后台协程)

如果是内部工具、访问量小的场景,不想额外搭Celery+Redis的全套组件,可以把Django切换到ASGI模式,用Django Channels在服务启动时单独起一个后台协程跑循环逻辑,每跑完一轮直接通过Channels的分组广播把结果推给前端连接的页面。
这个方案依赖少部署简单,但要注意协程里不能写阻塞式的同步代码,否则会卡整个ASGI事件循环,不适合高并发或者业务逻辑复杂的场景。


关键注意事项

  • 任务运行状态、结果不要存在Python全局变量里,Django多worker部署模式下进程间内存不共享,会出现标记位失效、结果读不到的问题,必须存在Redis、数据库这类多进程可共享的存储中。
  • 循环逻辑里必须加合理的sleep间隔,否则会占满100%CPU拖垮整个服务。
  • 启动、终止任务的接口必须加权限校验,避免未授权操作。
  • 如果用反向代理(比如Nginx),记得关闭SSE路径的代理缓冲,否则会出现消息堆积延迟的问题。

内容的提问来源于stack exchange,提问作者Jairus Matthias

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 00:48:20