C# MVC-并行任务中的System.Threading.ThreadAbortException
在我的MVC应用程序中,超级管理员可以设置任务队列,例如更新数据库。因此,当管理员向队列添加更新时,控制器将启动一个在后台工作的新任务。但是,当您添加一些任务时,应用程序会抛出C# MVC-并行任务中的System.Threading.ThreadAbortException,c#,.net,asp.net-mvc,multithreading,entity-framework,C#,.net,Asp.net Mvc,Multithreading,Entity Framework,在我的MVC应用程序中,超级管理员可以设置任务队列,例如更新数据库。因此,当管理员向队列添加更新时,控制器将启动一个在后台工作的新任务。但是,当您添加一些任务时,应用程序会抛出System.Threading.ThreadAbortException:线程被中止。此外,堆栈跟踪表明它发生在代码的不同行上 我还应该补充一点,任务使用EF6实体与SQL server一起工作,根据堆栈跟踪,它发生在对数据库执行操作之后或同时。由于更新通常很大,我使用db.Configuration.AutoDetec
System.Threading.ThreadAbortException:线程被中止
。此外,堆栈跟踪表明它发生在代码的不同行上
我还应该补充一点,任务使用EF6实体与SQL server一起工作,根据堆栈跟踪,它发生在对数据库执行操作之后或同时。由于更新通常很大,我使用db.Configuration.AutoDetectChangesEnabled=false
并每隔20k行手动保存更改,处理和重新创建数据库
堆栈跟踪的示例:
2015年7月15日星期三下午5:18:36:[报告]异常(第4566667行;第6节):System.Threading.ThreadAbortException:线程被中止。
在System.Array.Copy(数组源数组、Int32源索引、数组目标数组、Int32目标索引、Int32长度、布尔可靠)
at System.Collections.Generic.List`1.set_容量(Int32值)
位于System.Data.Entity.Core.Metadata.Edm.MetadataCollection`1.SetReadOnly()
at System.Data.Entity.Core.Metadata.Edm.TypeUsage..ctor(EdmType EdmType,IEnumerable`1 facets)
位于System.Data.Entity.Core.Common.CommandTrees.DbExpression..ctor(DbExpressionKind kind,TypeUsage type,Boolean forceNullable)
位于System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder.DbExpressionBuilder.PropertyFromMember(DbExpression实例,EdmMember属性,字符串propertyArgumentName)
位于System.Data.Entity.Core.Mapping.Update.Internal.UpdateCompiler.GenerateEqualityExpression(DbExpressionBinding目标、EdmProperty属性、PropagatorResult值)
位于System.Data.Entity.Core.Mapping.Update.Internal.UpdateCompiler.BuildPredicate(DbExpressionBinding目标、PropagatorResult referenceRow、PropagatorResult current、TableChangeProcessor处理器、Boolean&rowMustBeTouched)
位于System.Data.Entity.Core.Mapping.Update.Internal.UpdateCompiler.BuildUpdateCommand(PropagatorResult oldRow、PropagatorResult newRow、TableChangeProcessor处理器)
位于System.Data.Entity.Core.Mapping.Update.Internal.TableChangeProcessor.CompileCommand(ChangeNode ChangeNode,UpdateCompiler编译器)
在System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.d_ua.MoveNext()中
在System.Linq.Enumerable.d_u71`1.MoveNext()中
位于System.Data.Entity.Core.Mapping.Update.Internal.UpdateCommandOrderer..ctor(IEnumerable`1命令,UpdateTranslator转换器)
位于System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.ProduceCommands()处
位于System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()处
在System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 Func,IDBEcutionStrategy executionStrategy,Boolean startLocalTransaction,Boolean releaseConnectionOnSuccess)
位于System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions选项、IDBEcutionStrategy executionStrategy、Boolean startLocalTransaction)
在System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1操作)中
位于System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions选项,布尔值executeInExistingTransaction)
位于System.Data.Entity.Internal.InternalContext.SaveChanges()处
在MyWebsite.Controllers.AdminPanelController.ApplyUpdate(字符串文件路径、HttpApplicationStateBase上下文、Int32保存间隔、布尔校验重复、字符串冲突)
有什么我可能做错的吗?我想这可能与IIS空闲状态有关。您可以尝试每隔几分钟从另一个空闲线程使用基于自身的请求保持IIS运行后台线程。它将防止IIS中止另一个后台长时间运行的线程。每10分钟进行自我请求的方法示例:
public void KeepIisBackgroundThreadsAlive(Object stateInfo) {
while (true) {
var serverOwnIp = Dns.GetHostEntry(Dns.GetHostName()).AddressList.First(o => o.AddressFamily == AddressFamily.InterNetwork).ToString();
var req = (HttpWebRequest) WebRequest.Create(new Uri("http://" + serverOwnIp));
req.Method = "GET";
var response = (HttpWebResponse) req.GetResponse();
var respStream = response.GetResponseStream();
var delay = new TimeSpan(0, 0, 10, 0);
Thread.Sleep(delay);
}
}
然后,您可以在Application Started方法的Global.asax.cs中使用ThreadPool.QueueUserWorkItem方法启动此方法:
protected override void OnApplicationStarted(){
ThreadPool.QueueUserWorkItem(KeepIisBackgroundThreadsAlive)
}
重要更新
发现此方法仅适用于已禁用的IIS应用程序池回收。若您不熟悉此IIS功能,请仔细阅读本文。
请注意,你应该100%确定你在做什么
若你们在并没有关闭回收的情况下使用这种方法,你们可能会面临在第一次回收发生后网站不会启动的严重错误。对于这些需要应用程序池回收的网站,我强烈建议将长时间运行的作业移动到不依赖IIS的单独服务中(如果您使用Azure Web角色-至少移动到单独的工作者角色实例中).我认为,通过正确地将此功能抽象到另一个应用程序(更具体地说是Windows服务),从长远来看,您将获得更积极的结果。在我的公司,我编写了一个守护程序,它有长期运行/轮询的工作人员,它已经成为我们技术堆栈的重要组成部分4年多了。 这看起来像是一份工作日志,但它会回报你红利 顺便说一句,你所面临的实际问题;我同意@Vova的观点,因为您的应用程序是在IIS中托管的,IIS会做很多事情来确保您的应用程序不会导致服务器的其余部分停机。其中一些可能包括线程的终止 这里有几个链接,人们正在讨论你的问题(谷歌一点,你会发现更多):