【问题标题】:AsyncController used to run a console applicationAsyncController 用于运行控制台应用程序
【发布时间】:2014-12-10 20:07:45
【问题描述】:

有几个问题可以解决异步控制器的工作原理,但没有一个能完全满足我的需求。希望有人已经这样做了,如果我做错了什么,可以告诉我。

我有一个处理配置任务的 Web 应用程序和一个处理运行配置任务的控制台应用程序的组合。我希望能够通过单击按钮立即将控制权返回给用户并在后台执行任务来从 Web 应用程序运行任务。 AsyncController 似乎是完美的匹配。两个应用程序都使用 EF6、Unity 依赖注入和 SQL Server 2012 访问同一个数据库。Web 界面针对 MVC4 上的 .NET 4.5。

我可以毫无问题地完美运行控制台应用程序。我还可以使用以下代码从 Web 界面触发运行。唯一的问题是当通过 Web 应用程序触发时,任务将运行到某个点(我可以在日志 (Nlog) 中看到它)但它会停止执行,直到我重建解决方案 - 解决方案包含两个应用程序,这也将替换 .正在运行的exe。当我直接运行控制台应用程序时,我没有任何暂停。

这是我第一次使用 Task,我对 Process 类也有点害羞。如果我在做一些非常愚蠢的事情,请不要太苛刻。

这是控制器的代码:

public class TaskRunnerController : AsyncController
{
    private readonly ITaskProcessService _taskProcessService;
    private readonly IAuthenticationContext _context;
    private readonly IServiceBase<Task> _taskService;

    public TaskRunnerController(ITaskProcessService taskProcessService,
        IAuthenticationContext context,
        IServiceBase<Task> taskService)
    {
        _taskProcessService = taskProcessService;
        _context = context;
        _taskService = taskService;
    }

    private string TaskRunnerExe
    {
        get
        {
            var setting = ConfigurationManager.AppSettings["TaskRunner.Exe"];
            Guard.Against<ConfigurationErrorsException>(string.IsNullOrEmpty(setting), "Missing configuration setting: TaskRunner.Exe");
            return setting;
        }
    }

    [CustomAuthorize(typeof(CanRun))]
    public ActionResult RunTaskAsync(long id)
    {
        var task = _taskService.Find(i => i.TaskId == id);
        Guard.AgainstLoad(task, id);
        var fileInfo = new FileInfo(TaskRunnerExe);
        Guard.Against<ConfigurationErrorsException>(!fileInfo.Exists, "TaskRunner could not be found at specified location: {0}", TaskRunnerExe);
        var taskProcess = _taskProcessService.Schedule(task, _context.Principal.Identifier);
        AsyncManager.OutstandingOperations.Increment();
        System.Threading.Tasks.Task.Factory.StartNew(() =>
        {
            ProcessStartInfo info = new ProcessStartInfo(fileInfo.FullName,
                string.Format("{0} {1}", task.TaskId, taskProcess.TaskProcessId));
            info.UseShellExecute = false;
            info.RedirectStandardInput = true;
            info.RedirectStandardOutput = true;
            info.CreateNoWindow = true;
            var process = new Process { StartInfo = info, EnableRaisingEvents = true };
            process.Exited += (sender, e) =>
            {
                AsyncManager.OutstandingOperations.Decrement();
            };
            process.Start();
        });
        if (_context.Principal.HasPermission<CanViewList>())
            return RedirectToAction("Index", "Tasks");
        return RedirectToAction("Index", "Home");
    }

    public ActionResult RunTaskProgress()
    {
        return View();
    }

    public ActionResult RunTaskCompleted()
    {
        return Content("completed", "text/plain");
    }
}

依赖:

  • taskProcessService:每个任务运行事件的存储库
  • 上下文:保存当前用户的信息
  • taskService:任务存储库

控制台应用程序 (.exe) 在完全完成后退出。正如我所提到的,当通过 Web 应用程序调用时,该任务只会在我重建应用程序时完成 - 此时它会完成它应该做的一切 - 似乎它一直在工作,它只是在某个时候停止记录或报告在这个过程中。

也许这很重要 - 我在没有 Async 控制器的情况下再次尝试,但使用相同的设置 - 运行流程,包装在具有相同最终效果的任务中。我认为当主线程返回池时进程被杀死,这就是我尝试 AsyncController 的原因。

如果我打开任务管理器,我可以看到 exe 的进程在那里闲置。该应用程序记录了它的进度,但然后就坐在那里。这是VS的问题吗?我正在使用VS2012。

将流程包装到任务中我错了吗?有没有办法通过 Task 类和 Action 类直接运行可执行文件?

非常感谢您的任何见解。

【问题讨论】:

  • 只是事后的想法:我的印象是 AsyncController 应该正是为了这个目的——启动一个长时间运行的进程并将请求线程返回到池中。当进程完成时,从池中取出另一个线程来处理响应。这不是正在发生的事情吗?
  • 有点。您仍然需要等待该过程结束。不能结束请求,否则任务树会被清理。线程返回到池中,但您仍在等待任务完成。

标签: asp.net-mvc-4 process console-application executable asynccontroller


【解决方案1】:

应用程序很可能因为工作进程结束而终止。 IIS 将在请求线程完成后终止在请求线程下运行的任何线程或子进程。

因此,做你正在做的事情根本不可行。您可以将控制台应用程序移动到计划任务,然后从 Web 应用程序触发您的计划任务。

【讨论】:

  • 感谢 Erik 的洞察力。这正是我认为正在发生的事情。我会在某个时候通过 Windows 服务运行这些任务,但现在我只想让它工作。令人费解的是,我可以在任务管理器中看到该进程(没有被杀死) - 进程 ID 在它启动时被记录,因此我可以判断它是正确的。退出时的主线程是否只是杀死任务的线程而不是包装的进程?如前所述,重建后,该过程将恢复并正常完成。
【解决方案2】:

对此的更新。我现在已经确定由进程启动的 exe(控制台应用程序)确实在后台正常运行。但是,由于某种原因,当它由于某种原因被重定向时,它会停止写入标准输出流。我已经更新了正在运行的控制台应用程序,以便它在执行过程中将日志直接添加到数据库中的任务中,并且工作正常。我可以在网站上实时跟踪任务的进度。

我还验证了没有内存泄漏(控制台应用程序和网站都是托管代码)。 exe 完成后,它会按照我的预期从进程列表中删除。

重点是,上面的代码有效,但我仍然不确定为什么没有写入标准输出。这可能是因为在控制台应用程序中,我使用依赖注入的 Nlog 通过 ConsoleTarget 写入控制台。

【讨论】:

    猜你喜欢
    • 2011-01-22
    • 1970-01-01
    • 2013-02-11
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-30
    相关资源
    最近更新 更多