asp.net – 由TaskScheduler和SynchronizationContext混淆同步异步,无法控制同步
|
问题 我有一个ASP.NET 4.0 WebForms页面,其中包含一个简单的Web服务WebMethod.此方法用作异步/ TPL代码的同步包装器.我面临的问题是内部任务有时会有一个空的SynchronizationContext(我的首选项),但有时会有一个System.Web.LegacyAspNetSynchronizationContext的同步上下文.在我提供的示例中,这并不会导致问题,但在我的实际开发场景中可能导致死锁. 对服务的第一次调用似乎总是使用空同步上下文运行,接下来的几个也可能.但是一些快速请求它开始弹出到ASP.NET同步上下文. 代码 [WebMethod]
public static string MyWebMethod(string name)
{
var rnd = new Random();
int eventId = rnd.Next();
TaskHolder holder = new TaskHolder(eventId);
System.Diagnostics.Debug.WriteLine("Event Id: {0}. Web method thread Id: {1}",eventId,Thread.CurrentThread.ManagedThreadId);
var taskResult = Task.Factory.StartNew(
function: () => holder.SampleTask().Result,creationOptions: TaskCreationOptions.None,cancellationToken: System.Threading.CancellationToken.None,scheduler: TaskScheduler.Default)
.Result;
return "Hello " + name + ",result is " + taskResult;
}
TaskHolder的定义是: public class TaskHolder
{
private int _eventId;
private ProgressMessageHandler _prg;
private HttpClient _client;
public TaskHolder(int eventId)
{
_eventId = eventId;
_prg = new ProgressMessageHandler();
_client = HttpClientFactory.Create(_prg);
}
public Task<string> SampleTask()
{
System.Diagnostics.Debug.WriteLine("Event Id: {0}. Pre-task thread Id: {1}",_eventId,Thread.CurrentThread.ManagedThreadId);
return _client.GetAsync("http://www.google.com")
.ContinueWith((t) =>
{
System.Diagnostics.Debug.WriteLine("Event Id: {0}. Continuation-task thread Id: {1}",Thread.CurrentThread.ManagedThreadId);
t.Wait();
return string.Format("Length is: {0}",t.Result.Content.Headers.ContentLength.HasValue ? t.Result.Content.Headers.ContentLength.Value.ToString() : "unknown");
},scheduler: TaskScheduler.Default);
}
}
分析 我对TaskScheduler.Default的理解是它是ThreadPool调度程序.换句话说,该线程不会在ASP.NET线程上结束.根据this article,“任务并行库和PLINQ的默认调度程序使用.NET Framework ThreadPool来排队和执行工作”.基于此,我希望SampleTask中的SynchronizationContext始终为null. 此外,我的理解是,如果SampleTask位于ASP.NET SynchronizationContext上,则MyWebMethod中对.Result的调用可能会死锁. 因为我不会“一直向下”,所以这是一个“同步异步”的场景.由Stephen Toub撰写的每this article,在标题为“如果我确实需要的内容”的部分中同步异步“?”以下代码应该是一个安全的包装器: Task.Run(() => holder.SampleTask()).Result 根据this other article,同样由Stephen Toub,上述应该在功能上等同于: Task.Factory.StartNew(
() => holder.SampleTask().Result,CancellationToken.None,TaskCreationOptions.DenyChildAttach,TaskScheduler.Default);
感谢您使用.NET 4.0,我无法访问TaskCreationOptions.DenyChildAttach,我认为这是我的问题.但我在.NET 4.5中运行相同的示例并切换到TaskCreationOptions.DenyChildAttach并且它的行为相同(有时会抓取ASP.NET同步上下文). 我决定接近“原始”建议,并在.NET 4.5中实现: Task.Run(() => holder.SampleTask()).Result 这确实有效,因为它始终具有空同步上下文.哪种,有点暗示Task.Run vs Task.Factory.StartNew article有错呢? 实用的方法是升级到.NET 4.5并使用Task.Run实现,但这将涉及开发时间,我宁愿花在更紧迫的问题上(如果可能的话).另外,我还想知道不同的TaskScheduler和TaskCreationOptions场景发生了什么. 我巧合地发现.NET 4.0中的TaskCreationOptions.PreferFairness似乎表现得像我希望的那样(所有执行都有一个空同步上下文),但不知道为什么会这样,我对使用它非常犹豫(它可能不会在所有场景中工作). 编辑 一些额外的信息……我用一个做死锁的代码更新了我的示例代码,并包含一些调试输出以显示正在运行任务的线程.如果任务前或继续任务输出指示与WebMethod相同的线程ID,则会发生死锁. 奇怪的是,如果我不使用ProgressMessageHandler,我似乎无法复制死锁.我的印象是,这无关紧要,无论下游代码如何,我都应该能够使用正确的Task.Factory.StartNew或Task.Run方法在同步上下文中安全地“包装”异步方法.但似乎并非如此? 解决方法首先,在ASP.NET中使用sync-over-async通常没有多大意义.您将承担创建和安排任务的开销,但您无法以任何方式从中受益.现在,问你的问题:
好吧,ASP.NET也使用相同的ThreadPool.但这并不重要.有意义的是,如果您在计划运行的任务(但尚未启动)上等待()(或调用结果,这是相同的),则TaskScheduler我决定同步运行您的任务.这被称为“任务内联”. 这意味着您的任务最终在SynchronizationContext上运行,但实际上并没有通过它进行调度.这意味着实际上没有死锁的风险.
这与DenyChildAttach无关,没有任何可以成为AttachedToParent的任务.
这是因为PreferFairness将Task调度到全局队列(而不是每个ThreadPool线程所具有的线程本地队列),并且似乎不会内联全局队列中的Tasks.但我不会依赖这种行为,特别是因为它可以在将来改变. 编辑:
没有什么好奇的,这正是你的问题. ProgressMessageHandler报告当前同步上下文的进度.并且由于任务内联,即ASP.NET上下文,您通过同步等待来阻止它. 您需要做的是确保在没有设置同步上下文的线程上运行GetAsync().我认为最好的方法是在调用GetAsync()之前调用 (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc – 带MVC结果的Response.Flush无效
- asp.net-core – 找不到Swashbuckle.AspNetCore SwaggerOpe
- 模型绑定 – WebApi2:自定义参数绑定以绑定部分参数
- asp.net-mvc – ModelState.IsValid或Model.IsValid?
- asp.net – 使用Elmah处理Web服务中的异常
- asp.net – HttpContext.Current.User.Identity.Name始终是
- asp.net-mvc-4 – @ Html.Raw坚持编码引号
- asp.net-core – compilationOptions.emitEntryPoint是什么
- 有没有办法通过ASP.NET和app_offline.htm来显示图像?
- asp.net-mvc – 如何在ASP.NET MVC 3中更新复杂模型
- ASP.NET中的c# – die()或exit()功能
- ASP.NET奇怪的编译错误
- asp.net-mvc – 如何在Visual Studio 2017中构建
- asp.net – TableHeaderRow类的重点是什么
- asp.net – 经典ASP出站TLS 1.2
- asp.net-mvc – ASP.NET MVC使用自定义角色提供程
- asp.net-core – ASP.NET Core – Swashbuckle没
- asp.net – 如何在vNext项目中的方法上应用Outpu
- asp.net – 在aspnet核心1应用程序的逻辑层中获取
- asp.net – .NET身份电子邮件/用户名更改
