Web服务意外同步运行

本文关键字:运行 同步 意外 服务 Web | 更新日期: 2023-09-27 18:24:32

我们有一个web服务,它需要连接到一个缓慢的数据库进程并向客户端返回一个值,因此我们尝试异步运行它以阻止它阻塞线程。但是,如果我们进行两次调用,它们将按顺序运行,而不是异步运行。5秒后呼叫1返回,10秒后呼叫2,15秒后呼叫3,依此类推

这是(简化测试版本)代码-

public class ImportAsyncController : AsyncController
{
    [HttpPost]
    [Authorize(Roles = "Admin, ContentAdmin, ContentEdit")]
    public async Task<string> SlowProcess()
    {
        await Task.Delay(5000);
        return "Hello, world!";
    }
}

IIS 7.5,默认设置。遗留项目,所以不是完全不可能,项目文件中的其他地方有一些东西在强迫它,但不知道是什么,对不起。

作为参考,这是从调用SQL server存储过程中截取的真实代码。它在内部具有使用sp_getapplock的并发逻辑,并在SSMS中运行时正确返回-调用1运行到完成,如果调用1仍在运行,则调用>1返回错误代码;如果调用1未运行,则其本身完成。这种逻辑很好;真正的问题是web请求在队列中按顺序处理,所以在调用1完成之前甚至不会调用call>1。

我们做错了什么?

Web服务意外同步运行

如果您的操作是写入会话,就会发生这种情况。基本上,Session对象将被锁定,直到请求完成,这给人一种同步调用的错觉。

示例

由于以下操作写入会话,它将锁定会话对象,直到请求完成(5秒后)。如果您从客户端异步调用此操作,它将在5秒、10秒、15秒等之后返回。

public async Task<string> SlowProcess() {
    Session["Hello"] = "World";
    await Task.Delay(5000);
    return "Hello, world!"; 

}

解决方案

如果需要使用会话对象,可以告诉控制器使用ReadOnly模式,这样可以防止会话被锁定。为此,您可以在控制器上使用SessionStateBehavior属性(而不是操作)。

你会有这样的东西。。。

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class HomeController : Controller
{

    public async Task<string> SlowProcess()
    {
        Session["Hello"] = "World";
        await Task.Delay(5000);
        return "Hello, world!";
    }

}

希望这能有所帮助。

更新

我找到了一篇很好的文章来解释这个问题,你可能会觉得有用。看看吧!

http://johnculviner.com/asp-net-concurrent-ajax-requests-and-session-state-blocking/