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。
我们做错了什么?
如果您的操作是写入会话,就会发生这种情况。基本上,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/