【问题标题】:How to store the active status of a Task, and maintain permanency of a List of these Tasks如何存储任务的活动状态,并保持这些任务列表的永久性
【发布时间】:2018-03-27 17:42:36
【问题描述】:

我正在尝试准确了解由 QueueBackgroundWorkerItem 线程启动的任务的状态。我可以访问 Task 对象并将它们添加到我的 TaskModel 列表中,然后将该列表对象发送到我的视图。

无论我单击 QueueWorkItem 链接多少次并开始一项新任务,我的视图都只显示一个任务状态。我想弄清楚几件事:

  • 在 MVC 中,我如何保持我生成的任务数量的实时列表?我假设通过将模型发送到视图中,我会保证一些永久性。
  • 一旦我能做到这一点,我假设我能够将任务对象存储在我的列表中。但是,即使在下面的这个例子中,我似乎仍然无法在任何给定时间知道任务的状态(似乎我只能在将它添加到我的列表时知道它是什么) .

我希望有人做过类似的事情并且可以提供帮助。 谢谢! -杰森

编辑:此设置的核心要求是:

  • 即使浏览器关闭,我也需要使用 QueueBackgroundWorkerItem 来运行长时间的作业
  • 我选择嵌入一个任务,以便了解每个作业运行的持续状态。我知道使用 QBWI,运行任务将是矫枉过正。但我找不到其他方法随时了解 QBWI 的状态。

控制器:

List<TaskModel> taskModelList = new List<TaskModel>();

public ActionResult QueueWorkItem()
{
    Task task;
    ViewBag.Message = "State: ";
    String printPath = @"C:\Work\QueueBackgroundWorkerItemPractice\QueueBackgroundWorkerItemPractice\WorkerPrintFile" + DateTime.Now.ToLongTimeString().ToString().Replace(":", "_") + ".txt";
    System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
    {
        task = Task.Run(() =>
        {
            string filePath = printPath;
            string text = "File line ";
            if (!System.IO.File.Exists(filePath))
            {
                using (var stream = System.IO.File.Create(filePath)) { }
            }
            TextWriter tw = new StreamWriter(printPath);

            for (int i = 0; i < 400; i++)
            {
                text = "Line " + i;

                tw.WriteLine(text);

                Thread.Sleep(200);
            }

            tw.Close();
        });

        var c = task.ContinueWith((antecedent) =>
        {

            taskModelList.Add(new TaskModel(task));

        });

    });

    return View(taskModelList);
}

查看:

@model List<QueueBackgroundWorkerItemPractice.Models.TaskModel>

@{
    ViewBag.Title = "Queue Background Worker";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message<span id="modelClass"></span></h3>

<p>Use this area to provide additional information.</p>

@{ 
    <ul>
        @foreach (var taskModel in Model)
        {
            <li>@taskModel.Status</li>
        }
    </ul>
}

编辑,解决方案:

按照 Raffaeu 的建议和以下妥协,我能够找到它:

  • 我们在任何给定时间只运行一项任务。我不需要清单
  • 我并不真正需要按需状态。我只需要知道它什么时候完成

我希望能够利用任务 ID 稍后从 ID 实例化任务。事实证明,这会导致不必要的开销。

相反,我找到了 Task.CompletedTask 功能(在 .NET 4.6 及更高版本中可用)。这在异步中使用,允许我在任务完成时获取任务的状态。瞧。感谢大家的建议。

最好的部分 - 无论我关闭浏览器...或 停止 IIS,这个长期运行的任务都会完成。神奇。

public ActionResult QueueWorkItem()
{
    System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
    {
        task = Task.Run(async () =>
        {
            String printPath = @"C:...";
            string filePath = printPath;
            string text = "File line ";
            if (!System.IO.File.Exists(filePath))
            {
                using (var stream = System.IO.File.Create(filePath)) { }
            }
            TextWriter tw = new StreamWriter(printPath);

            for (int i = 0; i < 400; i++)
            {
                text = "Line " + i;

                tw.WriteLine(text);

                Thread.Sleep(200);
            }

            tw.Close();
            await Task.CompletedTask;
        });

        var c = task.ContinueWith((antecedent) =>
        {
            taskID = task.Id;
            status = task.Status.ToString();

            try
            {
                string connString = WebConfigurationManager.ConnectionStrings["TaskContext"].ToString();
                sqlCommand.CommandText = "INSERT INTO dbo.TaskTable (TaskId, Status) VALUES (" + taskID + ", '" + status + "')";
                conn.ConnectionString = connString;
                sqlCommand.Connection = conn;
                conn.Open();
                sqlCommand.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                String info = ex.Message + ex.ToString() + ex.StackTrace;

                throw new Exception("SQL Issue" + info);
            }
            finally
            {
                if (conn != null)
                {
                    conn.Close();
                    conn = null;
                }
                sqlCommand = null;
            }


        });

    });

    return View();
}

【问题讨论】:

  • 您的后台工作人员有点毫无意义,因为您在其中使用了Task.Run。摆脱Task.Run。此外,您是否知道每个请求都会获得一个新的控制器实例,所以每次发生的第一件事就是生成一个新的空 taskModelList。我会将其分解为一个更简单的问题,并且只需一个字符串列表,并在页面加载时进行处理。不涉及背景工作或任务。一旦你开始工作,尝试再次解决这个问题。
  • 感谢您的 cmets。请参阅对原始帖子的其他编辑。这可能会弄清楚我为什么选择我所做的设置。
  • 使用 signalr
  • @JasonPSallinger “即使浏览器关闭,我也需要使用 QueueBackgroundWorkerItem 来运行长时间的工作”,除非您的代码没有提供。您有一个作业将在浏览器窗口关闭时启动一个新任务,但该启动的任务未被 IIS 跟踪,因此它不会尝试使其自身保持活动状态保持它运行。从代码中删除 Task.Run。将您的继续代码放在队列函数之外,并将从 QueueBackgroundWorkItem 返回的任务添加到列表中。此外,您仍然需要解决列表不保存内容的问题。
  • @ScottChamberlain QueueBackgroundWorkItem 不返回任务。它实际上根本没有返回任何东西。

标签: c# asp.net iis task session-variables


【解决方案1】:

我认为你在这里遗漏了一点。

当您使用 MVC 时,您处于完全“无状态”的情况下,异步任务由不同的线程提交和执行,因此您无法控制它。您必须“获得通知”有关任务更改的唯一方法是从该任务中获取“事件”。一个事件将通知您“状态已更改”。 Task 类不引发事件,但具有 ContinueWith 方法。正确的方法是:

  1. 创建并启动Task 并将其Status 存储在某处(数据库、文本文件、会话)
  2. 使用ContinueWithTask 的状态上传到您的数据存储中
  3. 不断查询Datastore 以获取所有排队任务的Status 的更新版本

我认为在这个线程中他们试图实现相同的目标: How to track if an async/awaitable task is running

【讨论】:

  • 你不是一个完整的Stateless 情况,它只是建议你遵循那个架构。您可以创建一个有状态的组件,例如位于内存中的单例。
  • 谢谢你,拉法。按照您的建议,我想出了一个合适的解决方案。我会在 OP 上面贴出来。
猜你喜欢
  • 1970-01-01
  • 2020-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-10
  • 2014-06-03
相关资源
最近更新 更多