c# – 如何为各种代码块创建通用超时对象?
我有一系列代码块花了太长时间.它失败时我不需要任何技巧.事实上,我想在这些块花费太长时间时抛出异常,并且只是通过我们的标准错误处理而失败.我宁愿不为每个块创建方法(这是我到目前为止看到的唯一建议),因为它需要重写代码库.
如果可能的话,这就是我想要创建的内容. public void MyMethod( ... ) { ... using (MyTimeoutObject mto = new MyTimeoutObject(new TimeSpan(0,30))) { // Everything in here must complete within the timespan // or mto will throw an exception. When the using block // disposes of mto,then the timer is disabled and // disaster is averted. } ... } 我使用Timer类创建了一个简单的对象. (注意那些喜欢复制/粘贴的人:这个代码不起作用!!) using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Timers; public class MyTimeoutObject : IDisposable { private Timer timer = null; public MyTimeoutObject (TimeSpan ts) { timer = new Timer(); timer.Elapsed += timer_Elapsed; timer.Interval = ts.TotalMilliseconds; timer.Start(); } void timer_Elapsed(object sender,ElapsedEventArgs e) { throw new TimeoutException("A code block has timed out."); } public void Dispose() { if (timer != null) { timer.Stop(); } } } 它不起作用,因为System.Timers.Timer类捕获,吸收和忽略在其中抛出的任何异常,正如我所发现的那样 – 击败了我的设计.在没有完全重新设计的情况下创建此类/功能的任何其他方式? 两个小时前这似乎很简单,但让我很头疼. 解决方法
好的,我花了一些时间在这个上面,我想我有一个适合你的解决方案,而不必更改你的代码.
以下是如何使用我创建的 public void MyMethod( ... ) { // some stuff // instead of this // using(...){ /* your code here */ } // you can use this var timebox = new Timebox(TimeSpan.FromSeconds(1)); timebox.Execute(() => { /* your code here */ }); // some more stuff } 以下是Timebox的工作原理. >使用给定的Timespan创建Timebox对象 缺点是您的程序需要足够高的权限才能创建AppDomain. 这是一个示例程序,演示它是如何工作的(我相信如果包含正确的用法,你可以复制粘贴它).如果你有兴趣,我也创建了this gist. public class Program { public static void Main() { try { var timebox = new Timebox(TimeSpan.FromSeconds(1)); timebox.Execute(() => { // do your thing for (var i = 0; i < 1000; i++) { Console.WriteLine(i); } }); Console.WriteLine("Didn't Time Out"); } catch (TimeoutException e) { Console.WriteLine("Timed Out"); // handle it } catch(Exception e) { Console.WriteLine("Another exception was thrown in your timeboxed function"); // handle it } Console.WriteLine("Program Finished"); Console.ReadLine(); } } public class Timebox { private readonly TimeSpan _ts; public Timebox(TimeSpan ts) { _ts = ts; } public void Execute(Action func) { AppDomain childDomain = null; try { // Construct and initialize settings for a second AppDomain. Perhaps some of // this is unnecessary but perhaps not. var domainSetup = new AppDomainSetup() { ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,LoaderOptimization = LoaderOptimization.MultiDomainHost }; // Create the child AppDomain childDomain = AppDomain.CreateDomain("Timebox Domain",null,domainSetup); // Create an instance of the timebox runtime child AppDomain var timeboxRuntime = (ITimeboxRuntime)childDomain.CreateInstanceAndUnwrap( typeof(TimeboxRuntime).Assembly.FullName,typeof(TimeboxRuntime).FullName); // Start the runtime,by passing it the function we're timboxing Exception ex = null; var timeoutOccurred = true; var task = new Task(() => { ex = timeboxRuntime.Run(func); timeoutOccurred = false; }); // start task,and wait for the alloted timespan. If the method doesn't finish // by then,then we kill the childDomain and throw a TimeoutException task.Start(); task.Wait(_ts); // if the timeout occurred then we throw the exception for the caller to handle. if(timeoutOccurred) { throw new TimeoutException("The child domain timed out"); } // If no timeout occurred,then throw whatever exception was thrown // by our child AppDomain,so that calling code "sees" the exception // thrown by the code that it passes in. if(ex != null) { throw ex; } } finally { // kill the child domain whether or not the function has completed if(childDomain != null) AppDomain.Unload(childDomain); } } // don't strictly need this,but I prefer having an interface point to the proxy private interface ITimeboxRuntime { Exception Run(Action action); } // Need to derive from MarshalByRefObject... proxy is returned across AppDomain boundary. private class TimeboxRuntime : MarshalByRefObject,ITimeboxRuntime { public Exception Run(Action action) { try { // Nike: just do it! action(); } catch(Exception e) { // return the exception to be thrown in the calling AppDomain return e; } return null; } } } 编辑: 之所以我使用AppDomain而不是仅使用线程或任务,是因为没有用于终止任意代码的线程或任务的防弹方式[2] [3].根据您的要求,AppDomain对我来说似乎是最好的方法. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- if-statement – VB6 IIf的优势
- postgresql – 对postgres和postmaster服务的高CPU使用率进
- c# – 适用于.NET Standard 1.x的Type.GetInterfaces()变通
- 如何把List<T>转成XML操作、增删改查
- oracle – 如何找到导致ora-06575的错误?
- cocos2dx3.x 新手打包APK注意事项!
- DDD:订单管理 之 如何组织代码
- swift3.0-屏蔽系统日记打印
- ruby-on-rails – Rails4 ActionController :: InvalidAuth
- AJAX 跨域请求 - JSONP获取JSON数据