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

MVC中如何存储Task活跃状态、维护任务列表持久化及实时展示?

解决MVC中后台Task状态跟踪与实时列表展示问题

我之前在做后台任务状态监控的时候也踩过类似的坑,咱们一步步来拆解你的两个核心问题:


1. 存储Task活跃状态并维护任务列表的持久性

首先得明确一个关键:MVC的控制器是每次请求都会实例化新对象的,如果你只是在控制器里定义一个普通的List<TaskModel>,每次请求都会重置集合,这就是为什么你始终只能看到一个任务状态的核心原因。要解决这个,得用全局共享且线程安全的存储容器,同时根据需求选择是否持久化:

方案一:内存级线程安全集合(适合临时任务,重启后丢失)

用System.Collections.Concurrent命名空间下的线程安全集合,比如ConcurrentDictionary<Guid, TaskModel>——给每个任务分配唯一Guid作为键,能方便跟踪单个任务的状态变化:

// 在控制器类中定义静态的线程安全集合,全局共享
private static readonly ConcurrentDictionary<Guid, TaskModel> _activeTasks = new ConcurrentDictionary<Guid, TaskModel>();

// 启动后台任务的Action
public ActionResult QueueWorkItem()
{
    var taskId = Guid.NewGuid();
    var taskModel = new TaskModel 
    { 
        Id = taskId, 
        Status = TaskStatus.Running, 
        Name = $"后台任务 {taskId:N}" 
    };
    
    // 将任务模型加入集合
    _activeTasks.TryAdd(taskId, taskModel);

    // 启动后台任务
    HostingEnvironment.QueueBackgroundWorkItem(async ct =>
    {
        try
        {
            // 模拟任务执行逻辑
            await Task.Delay(8000, ct);
            // 任务完成后更新状态
            taskModel.Status = TaskStatus.RanToCompletion;
        }
        catch (OperationCanceledException)
        {
            taskModel.Status = TaskStatus.Canceled;
        }
        catch (Exception)
        {
            taskModel.Status = TaskStatus.Faulted;
        }
    });

    return RedirectToAction("TaskList");
}

方案二:持久化存储(适合跨应用重启保留的任务)

如果你的任务需要在应用池回收、服务器重启后还能跟踪状态,那就得把任务信息持久化到数据库或者Redis中:

  • 任务启动时,向存储介质插入一条记录,标记状态为Running;
  • 任务执行过程中(如果有进度需求),定时更新进度字段;
  • 任务完成/失败/取消时,同步更新状态字段;
  • 读取任务列表时,直接从存储介质查询即可。

2. 在MVC中保持已生成任务的实时列表

MVC默认是请求响应模式,页面加载后不会自动更新,要实现实时列表,有两种常用方案:

方案一:AJAX轮询(简单易实现)

前端用setInterval定时向控制器请求最新的任务列表,然后更新页面UI,适合低频率更新的场景:

后端Action(提供任务列表接口)

public JsonResult GetTaskList()
{
    // 从线程安全集合或数据库获取最新任务列表
    var tasks = _activeTasks.Values.ToList();
    return Json(tasks, JsonRequestBehavior.AllowGet);
}

前端代码(Razor视图中)

<div id="taskListContainer"></div>

<script>
    // 每3秒刷新一次任务列表
    setInterval(() => {
        $.get("/Home/GetTaskList", function(tasks) {
            let listHtml = "<ul>";
            tasks.forEach(task => {
                listHtml += `<li>任务ID: ${task.Id} - 当前状态: ${task.Status}</li>`;
            });
            listHtml += "</ul>";
            $("#taskListContainer").html(listHtml);
        });
    }, 3000);
</script>

方案二:SignalR实时推送(真正的实时更新)

如果需要任务状态一变化就立刻推给前端,推荐用SignalR,它能建立服务器与客户端的双向通信,适合高实时性需求的场景:

1. 安装SignalR包

Install-Package Microsoft.AspNet.SignalR

2. 创建SignalR Hub类

public class TaskStatusHub : Hub
{
    // 客户端调用此方法获取初始任务列表
    public List<TaskModel> GetCurrentTasks()
    {
        return _activeTasks.Values.ToList();
    }
}

3. 任务状态更新时推送消息

在后台任务状态变化时,主动推送给所有客户端:

// 任务完成后更新状态并推送
taskModel.Status = TaskStatus.RanToCompletion;
// 推送给所有在线客户端
GlobalHost.ConnectionManager.GetHubContext<TaskStatusHub>().Clients.All.updateTaskList(_activeTasks.Values.ToList());

4. 前端接收推送并更新UI

<div id="taskListContainer"></div>

<script src="/Scripts/jquery.signalR-2.4.3.min.js"></script>
<script src="/signalr/hubs"></script>
<script>
    var taskHub = $.connection.taskStatusHub;

    // 定义接收服务器推送的方法
    taskHub.client.updateTaskList = function(tasks) {
        let listHtml = "<ul>";
        tasks.forEach(task => {
            listHtml += `<li>任务ID: ${task.Id} - 当前状态: ${task.Status}</li>`;
        });
        listHtml += "</ul>";
        $("#taskListContainer").html(listHtml);
    };

    // 启动连接并获取初始任务列表
    $.connection.hub.start().done(function() {
        taskHub.server.getCurrentTasks().done(function(tasks) {
            taskHub.client.updateTaskList(tasks);
        });
    });
</script>

最后补充个小提醒:用HostingEnvironment.QueueBackgroundWorkItem启动的后台任务,在应用池回收时会被强制终止,如果需要更可靠的后台任务管理,推荐用Hangfire这类专门的后台任务框架,它自带任务持久化、重试机制、状态跟踪等功能,能省不少开发成本。

内容的提问来源于stack exchange,提问作者Jason P Sallinger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:32:50