Task.Factory.StartNew在长时间运行的过程中阻止对其他方法的调用

时间:2019-06-18 11:14:25

标签: c# asp.net-mvc async-await task

我在ASP.NET MVC项目(产品应用)中有一个操作方法,该方法可以导入csv文件记录并在进度条上显示进度,为此我使用了https://stackoverflow.com/a/2928148/1386991,这对于全新的MVC项目效果很好(POC应用)。

但是,当我在原始项目(产品应用)中放置完全相同的控制器并查看(带有js代码)时,CheckTaskProgress方法仅在RunLongTask完成任务时才会命中,因此用户看不到进度条更新并突然发生100%的变化,这与我在全新项目(POC应用程序)中的行为完全相反。这是可见的行为屏幕截图,供您参考https://www.youtube.com/watch?v=wNa5De0lmdA

我检查了MVC软件包的版本和web.config,看起来不错。有任何检查线索吗?

这是控制器代码:

using System;
using System.Web.Mvc;
using System.Threading.Tasks;

namespace Prod.Web.Controllers
{
    public class DemoController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public async Task<ActionResult> RunLongTask()
        {
            string id = "task_id1";  //should be unique
            int maxcount = 200;

            AsyncManager.OutstandingOperations.Increment();
            await Task.Factory.StartNew(taskId =>
            {
                HttpContext.Application["t_max_" + taskId] = maxcount;
                for (int i = 0; i < maxcount; i++)
                {
                    System.Threading.Thread.Sleep(100);
                    HttpContext.Application["t_prog_" + taskId] = i;
                }
                AsyncManager.OutstandingOperations.Decrement();
            }, id);

            return Json(new { status = true, ProgressCurrent = maxcount, ProgressMax = maxcount, ProgressPercent = 100 });
        }

        public ActionResult CheckTaskProgress()
        {
            string id = "task_id1";  //should be unique

            var progressCurrent = HttpContext.Application["t_prog_" + id];
            var progressMax = HttpContext.Application["t_max_" + id];
            decimal progressPercent = (Convert.ToDecimal(progressCurrent) / Convert.ToDecimal(progressMax)) * 100M;

            return Json(new
            {
                ProgressCurrent = progressCurrent,
                ProgressMax = progressMax,
                ProgressPercent = Convert.ToInt32(progressPercent)
            }, JsonRequestBehavior.AllowGet);
        }
    }
}

这是js代码的视图:

@{
    ViewBag.Title = "Run ajax long running process and display on progress bar";
}
<div class="main-wrap">
    <div class="container">
        <div class="row py-5">
            <div class="col-12">
                <button class="btn btn-primary runlongtask">
                    <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="display:none;"></span>
                    Run ajax long running process and display on progress bar
                </button>
            </div>
            <div class="col-12 pt-5">
                <div class="progress">
                    <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="0" style="width:0%"></div>
                </div>
            </div>
        </div>
    </div>
</div>
@section foot{
    <script>
        $(function () {

            $('.runlongtask').click(function () {
                var triggerbtn = $(this);
                triggerbtn.prop('disabled', true).children('.spinner-border').show();
                $.ajax({
                    type: 'POST',
                    url: '/demo/runlongtask',
                    success: function (data) {
                        if (data.status) {
                            longTaskUpdateProgress(data.ProgressCurrent, data.ProgressMax, data.ProgressPercent);
                            triggerbtn.prop('disabled', false).children('.spinner-border').hide();
                            window.clearInterval(longTaskInterval);
                        }
                    }
                });

                var longTaskInterval = window.setInterval(function () {
                    console.log('interval func invoked');
                    $.ajax({
                        type: 'GET',
                        url: '/demo/checktaskprogress',
                        success: function (data) {
                            longTaskUpdateProgress(data.ProgressCurrent, data.ProgressMax, data.ProgressPercent);
                        }
                    });
                }, 5000);

                var longTaskUpdateProgress = function (ProgressCurrent, ProgressMax, ProgressPercent) {
                    console.log('progressbar func invoked');
                    $('.progress-bar')
                        .attr('aria-valuenow', ProgressPercent)
                        .css('width', ProgressPercent + '%')
                        .html(ProgressPercent + '% Completed');

                    console.log(ProgressPercent + ' Completed');
                    console.log(ProgressCurrent + ' executed out of ' + ProgressMax);
                };
            });

        })
    </script>
}

这是我在GitHub https://github.com/itorian/Run-Ajax-Long-Process-With-Progress-Bar上的项目(POC应用程序)的源代码。

2 个答案:

答案 0 :(得分:3)

有两种可能:

  1. 您现有的项目使用会话状态。在这种情况下,您需要为这些方法选择退出会话状态;否则,CheckTaskProgress调用将一直阻塞,直到RunLongTask完成,因为它正在等待会话可用。
  2. 您现有的项目has not been updated for async/await。在这种情况下,您应该添加一个适当的httpRuntime targetFramework值。

旁注:

  • 您使用的答案来自二十年前。今天有更好的解决方案。例如SignalR。
  • 此解决方案无法在Web场中正常工作,因为任务状态都保存在内存中。更好的解决方案是将任务状态存储在某个共享的位置。
  • 您应避免使用StartNew。我什至不确定为什么它在原始代码中。无需进行StartNewAsyncManager调用,它应该可以正常工作。

答案 1 :(得分:1)

不建议将异步代码和非异步(阻止)代码混合使用,因此以下语句将引起问题:

=SUBSTITUTE(CONCATENATE(D1,">",E1,">",F1,">",G1,">",H1), ">>", ">")

替换为:

System.Threading.Thread.Sleep(100);
  

经验法则是一直向下同步

参考:
When to use Task.Delay, when to use Thread.Sleep?