【问题标题】:Task.Wait locks the flow of the controller in MVC5Task.Wait 在 MVC5 中锁定控制器的流
【发布时间】:2014-08-06 23:51:13
【问题描述】:

我们正在使用来自 MVC 站点的 WebAPI。我们从这样的控制台应用程序进行了概念验证,使用 AddUser 方法,该方法在我们的数据库中注册一个新用户。

static void Main()
    {
        M_Users user = new M_Users();

        var newUser = new M_Users()
        {
            Password = "123",
            FirstName = "Andrew",
            Email = "someAndrew@someplace.com",
            AdminCode = 1,
            IsDisabled = false,
            CountryId = 48,
            CityId = 149,
            DepartmentId = 3,
            CreationDate = DateTime.Now,
            CreatorUserId = 1
        };
        var star = RunAsync(newUser);
        star.Wait();

        //THIS LINE IS REACHED ALWAYS
        Console.WriteLine(star.Result.UserID);
        Console.ReadLine();
    }

    static async Task<M_Users> AddUserAsync(M_Users newUser)
    {
        M_Users user = new M_Users();
        string requestUri = "api/User/AddUser";

        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://localhost:44873/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            // HTTP POST
            HttpResponseMessage response = await client.PostAsJsonAsync<M_Users>(requestUri, newUser);
            if (response.IsSuccessStatusCode)
            {
                Uri newUserUrl = response.Headers.Location;
                user = await response.Content.ReadAsAsync<M_Users>();
            }

            return user;
        }
    }

这个 AddUserAsync 方法在两种情况下调用完全相同,控制台应用程序和 MVC 应用程序,以及 biz 层中的方法也是相同的,这里没有描述似乎不相关。

在控制台中它刚刚工作。用户已注册,控制台打印了用户在 BD 中保存的 ID。这几乎就是我们需要证明的。我们可以从同步方法调用异步方法。现在我们从我们网站的控制器中尝试相同的操作,就像这样。

    public ActionResult Index()
    {
        User spUser = null;

        var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
        var model = new LoginViewModel();

        // here we invoke sharepoint context for getting user info to bild the object

        M_Users new_user = new M_Users()
                    {
                        Password = "123",
                        FirstName = spUser.Title,
                        Email = spUser.LoginName.Split('|')[2].ToString(),
                        AdminCode = 1,
                        IsDisabled = false,
                        CountryId = 48,
                        CityId = 149,
                        DepartmentId = 3, 
                        CreationDate = DateTime.Now,
                        CreatorUserId = 1
                    };


                    var returnedUser = AddUserAsync(new_user);
                    returnedUser.Wait(); //HERE IT STOPS RUNNING

                    //THIS LINE IS NEVER REACHED
                    ViewBag.UserId = returnedUser.Result.UserID;

                    return View(model);
    }

需要做什么才能继续执行。

我们也尝试了 ContinueWith() 并且它运行了,但是 async 方法没有被执行并且数据库中没有任何记录。更具体地说,该方法被调用,但它似乎越过了它并且没有返回任何内容。 :(

【问题讨论】:

  • 这是使用异步的常见问题之一。请参阅 Stephen Cleary 的帖子blog.stephencleary.com/2012/07/dont-block-on-async-code.html。解决死锁的一种方法是像public async Task&lt;ActionResult&gt; Index 这样更改操作方法。然后,使用var result = await returnedUser;,而不是returnedUser.Wait();

标签: c# asp.net-web-api asp.net-mvc-5 async-await


【解决方案1】:

这几乎就是我们需要证明的。我们可以通过同步方法调用异步方法。

这是错误的教训。控制台应用程序使用与 ASP.NET 不同的线程框架,这就是让您感到困惑的原因。

我详细解释on my blog;它的要点是await 捕获一个“上下文”并使用它来恢复async 方法。在控制台应用程序中,这个“上下文”是线程池上下文,因此async 方法会继续在某个任意线程池线程上运行,并且不关心您是否阻塞了调用Wait 的线程。但是,ASP.NET 有一个请求上下文,一次只允许一个线程,因此async 方法试图在该请求上下文中恢复,但它不能,因为该上下文中已经有一个线程被阻塞。

最好的解决方案是让async 在代码库中增长:

public Task<ActionResult> Index()
{
  User spUser = null;
  var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
  var model = new LoginViewModel();
  M_Users new_user = new M_Users()
  {
    Password = "123",
    FirstName = spUser.Title,
    Email = spUser.LoginName.Split('|')[2].ToString(),
    AdminCode = 1,
    IsDisabled = false,
    CountryId = 48,
    CityId = 149,
    DepartmentId = 3, 
    CreationDate = DateTime.Now,
    CreatorUserId = 1
  };

  var returnedUser = await AddUserAsync(new_user);
  ViewBag.UserId = returnedUser.UserID;
  return View(model);
}

【讨论】:

    猜你喜欢
    • 2019-04-07
    • 2014-11-28
    • 1970-01-01
    • 1970-01-01
    • 2017-10-04
    相关资源
    最近更新 更多