在我的MVC 3应用程序中,我需要每5分钟调用一次动作控制器。 在此操作控制器中,我将读取并解析XML文件。 所以当我有很多客户端同时调用action方法时,我会得到IO异常。我怎样才能避免这个问题并使我的动作调用线程安全。 我的控制器动作如下所示:
public PartialViewResult _warningsView(string containerId)
{
Debug.WriteLine("_warningsView");
var myXslTrans = new XslCompiledTransform();
myXslTrans.Load(@"E:/web/data/xml/alert.xslt");
myXslTrans.Transform(@"E:/.../alert.xml", @"E:/.../TransAlert.xml");
XmlSerializer serializer = new XmlSerializer(typeof(warnings));
using (FileStream fileStream = new FileStream(@"E:/.../TransAlert.xml", FileMode.Open))
{
warnings result = (warnings)serializer.Deserialize(fileStream);
return new Ext.Net.MVC.PartialViewResult
{
RenderMode = RenderMode.AddTo,
ContainerId = containerId,
Model = result.warningList,
WrapByScriptTag = false
};
}
}
我忘了提到错误:
[IOException: The process cannot access the file 'E:\web\data\xml\TransAlert.xml' because it is being used by another process.]
System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) +10527069
System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost) +1305
System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy) +60
System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean useAsync) +54
System.Xml.XmlWriterSettings.CreateWriter(String outputFileName) +155
System.Xml.XmlWriter.Create(String outputFileName, XmlWriterSettings settings) +23
System.Xml.Xsl.XslCompiledTransform.Transform(String inputUri, String resultsFile) +91
WIS_3_0.Controllers.HomeController._warningsView(String containerId) in C:\Users\Mohamed\Documents\Visual Studio 2010\Projects\wis30\WIS_3_0\Controllers\HomeController.cs:92
lambda_method(Closure , ControllerBase , Object[] ) +180
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +14
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +214
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
System.Web.Mvc.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12() +55
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +253
System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +21
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +253
System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +21
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +191
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +324
System.Web.Mvc.Controller.ExecuteCore() +106
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +91
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +34
System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +19
System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +10
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +48
System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9514928
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
答案 0 :(得分:1)
您可以通过在the constructor中指定FileShare
参数,同时允许多个线程同时读取文件:
using (FileStream fileStream = new FileStream(@"E:/.../TransAlert.xml",
FileMode.Open, FileAccess.Read, FileShare.Read))
{
// Your code here
}
每 Quintium 的注释,这是代码当前构造函数调用的默认行为。因此,异常可能与您的程序的另一个方面有关。当您提供例外情况时,我和/或其他人将能够为您提供进一步的帮助。
根据例外情况编辑
您提供的错误[IOException: The process cannot access the file 'E:\web\data\xml\TransAlert.xml' because it is being used by another process.]
表示该文件已被其他线程或进程锁定。在这种情况下,堆栈跟踪告诉故事:
System.Xml.Xsl.XslCompiledTransform.Transform(String inputUri, String resultsFile) +91
这表示此行可能是您的问题:
myXslTrans.Transform(@"E:/.../alert.xml", @"E:/.../TransAlert.xml");
我相信Transform
会导致WRITE操作(尽管我从未使用过它)。这会被导致此异常的其他READING操作阻止。
要快速,轻松地解决这个问题,您需要使用经典的文件访问方法:自旋锁。
旋转锁定:
最简单的方法是将您的尝试包装在try-catch块中,如果捕获到异常,则休眠X时间,然后重试写入。这是检查文件系统访问的唯一“原子”方式。
另一种常见方法是在写入时将标记文件(例如“正在工作”)写入文件夹。然后,在让读者做任何工作之前检查它(并旋转锁定并再次检查,直到它被移除)。编写者在完成写入时将删除标记文件,允许读者再次访问。
请注意,由于检查文件访问和实际访问文件之间的竞争条件,编写器和读取器都需要包装在try-catch-spin模式中。锁定文件内存是不够的,因为如果你去过&gt; 1个Web应用程序实例,锁定将按进程而不是每个文件。
答案 1 :(得分:1)
您有多个请求写入同一个文件!如果您坚持使用文件输出,那么您应该写入临时文件(每个请求的唯一文件名)。
更好的方法是写入MemoryStream。