一种延迟方法调用的实现

原文:一种延迟方法调用的实现

需求场景

最近遇到一种场景,需要延迟某个步骤的实现,以便在延迟的过程中能够及早处理,从而取消任务。 比如,现在的服务器CPU过高了,则系统记录下来,然后开始发送邮件通知用户,但是如果在10秒之内CPU恢复了;或者我不希望用户得知这个CPU的信息,因为我就在现场,我可以及时知道怎么处理这种情况,这个时候准备拨打电话的这个方法的调用就不能被执行,需要延迟一段时间,以便能够被取消——以上场景仅供参考。

代码实现

以下是我的一个实现方式,供大家讨论,或者有更好的方式可以改进。

这种方法旨在能够方便的延迟任意方法的执行:

定义任务接口

这是一个基本的线程任务的接口,包括任务名称、线程的运行状态、启停线程。

1     interface ITask
2     {
3         string Name { get; }
4         bool IsRunning { get; }
5         void Start();
6         void Stop();
7         void Run();

8     }

定义延迟的信息类

保存当前需要延迟的方法,以及需要延迟的时间等

1         class LazyItem

2         {
 3             private readonly int _timeout;
 4             private readonly DateTime _enqueueTime;
 5 
 6             public bool IsTimeout
 7             {
 8                 get { return (DateTime.Now - _enqueueTime).TotalSeconds > _timeout; }
 9             }
10             public string Key { get; private set; }
11             public Action Continue { get; private set; }
12 
13             public LazyItem(string key, int timeout, Action continueAction)
14             {
15                 _enqueueTime = DateTime.Now;
16                 _timeout = timeout;
17                 Key = key;
18                 Continue = continueAction;
19             }
20         }

延迟的实现

主要是执行一个定时的任务,去检测被延迟的方法是否达到了超时的时间,如果达到,则执行该方法。另外,提供一个可供取消方法的函数。

1     class LazyInvoker : ITask

2     {
 3         public string Name
 4         {
 5             get { return "Lazy Invoke Task"; }
 6         }
 7 
 8         public bool IsRunning { get; private set; }
 9 
10         private readonly ConcurrentDictionary<string, LazyItem> _lazyActions = new ConcurrentDictionary<string, LazyItem>();
11 
12         public void Start()
13         {
14             Task.Factory.StartNew(Run);
15             IsRunning = true;
16         }
17 
18         public void Stop()
19         {
20             IsRunning = false;
21         }
22 
23         /// <summary>
24         /// 检测被延迟的任务,达到超时时间则触发
25         /// </summary>
26         /// Created by:marvin(2014/10/11 12:14)
27         public void Run()
28         {
29             while (IsRunning)
30             {
31                 CheckContinue();
32                 Thread.Sleep(1 * 1000);
33             }
34         }
35 
36         private void CheckContinue()
37         {
38             if (_lazyActions.Count <= 0) return;
39 
40             var removeKeys = (from lazyItem in _lazyActions where lazyItem.Value.IsTimeout select lazyItem.Key).ToList();
41             if (removeKeys.Count <= 0) return;
42 
43             foreach (var key in removeKeys)
44             {
45                 LazyItem tmp;
46                 if (_lazyActions.TryRemove(key, out tmp))
47                 {
48                     tmp.Continue();
49                 }
50             }
51         }
52 
53         /// <summary>
54         /// 延迟工作
55         /// </summary>
56         /// <param name="id">The identifier.</param>
57         /// <param name="lazyTimeout">The lazy timeout.</param>
58         /// <param name="continueAction">The continue action.</param>
59         /// Created by:marvin(2014/10/11 11:48)
60         public void LazyDo(string id, int lazyTimeout, Action continueAction)
61         {
62             if (!_lazyActions.ContainsKey(id))
63             {
64                 if (_lazyActions.TryAdd(id, new LazyItem(id, lazyTimeout, continueAction)))
65                 {
66                     //Console.WriteLine("lazy action : {0} , timeout : {1}", id, lazyTimeout);
67                 }
68             }
69         }
70 
71         /// <summary>
72         /// 取消任务
73         /// </summary>
74         /// <param name="actionKey">The action key.</param>
75         /// Created by:marvin(2014/10/11 12:02)
76         public void Cancel(string actionKey)
77         {
78             if (_lazyActions.ContainsKey(actionKey))
79             {
80                 LazyItem tmp;
81                 if (_lazyActions.TryRemove(actionKey, out tmp))
82                 {
83                     Console.WriteLine("lazy action “{0}” had removed", tmp.Key);
84                 }
85             }
86         }
87     }

 测试

1     class Program

2     {
 3         static void Main(string[] args)
 4         {
 5             var lazyInvoker = new LazyInvoker();
 6             lazyInvoker.Start();
 7 
 8             //延迟7秒运行
 9             lazyInvoker.LazyDo(Guid.NewGuid().ToString(), 7, DoSomething);
10             Thread.Sleep(5 * 1000);
11 
12             //延迟3秒运行,但是3秒的时候被取消
13             var id = Guid.NewGuid().ToString();
14             lazyInvoker.LazyDo(id, 5, DoSomething);
15             Thread.Sleep(3 * 1000);
16             lazyInvoker.Cancel(id);
17 
18             Console.ReadKey();
19         }
20 
21         private static void DoSomething()
22         {
23             Console.WriteLine("Now time is :" + DateTime.Now.ToString("HH:mm:ss"));
24         }
25     }

运行结果如下:

时间: 2024-10-05 04:58:33

一种延迟方法调用的实现的相关文章

练习PopupWindow弹出框之实现界面加载的时候显示弹出框到指定的view下面--两种延迟方法

今天在练习PopupWindow弹出框的时候,打算在界面加载的时候将弹出框展现出来并显示在指定的view下面. 初步方法是直接在OnResume方法里面直接执行showPopupWindows方法. 但是报“Unable to add window -- token null is not valid; is your activity running?” 原因参考:http://cb269267.iteye.com/blog/1787779 总结下原因如下:popupwindow必须要指定一个

两种冒泡排序方法调用的修改--一个方法同时实现两种排序

class Program { /// <summary> /// 数组排序 /// </summary> /// <param name="arr">数组</param> /// <param name="b">true为从小到大排列 false为从大到小排列</param> /// <returns>排序完的数组</returns> static int[] Meth

DataContext.ExecuteQuery的两种方法调用

ExecuteQuery主要用于DataContext类直接执行SQL语句的查询,在MSDN上有两种执行方法,下面为两种方法的不同调用: 1.ExecuteQuery<TResult>(String, Object[])   应该是微软的推荐方法,网上几乎都在用的方法 NorthwindDataContext ctx = new NorthwindDataContext("server=xxx;database=Northwind;uid=xxx;pwd=xxx"); st

第164天:js方法调用的四种模式

js方法调用的四种模式 1.方法调用模式 1 function Persion() { 2 var name1 = "itcast", 3 age1 = 19, 4 show1 = function() { 5 console.log(this.name); 6 }; 7 8 return { 9 age : age1, 10 name : name1, 11 show : show1 12 }; 13 } 14 15 var p = new Persion(); 16 p.show(

基于Apache+Tomcat负载均衡的两种实现方法

Apache+Tomcat实现负载均衡的两种实现方法 如果我们将工作在不同平台的apache能够实现彼此间的高效通信,因此它需要一种底层机制来实现--叫做apr Apr的主要目的就是为了其能够让apache工作在不同的平台上,但在linux上安装apache的时候通常都是默认安装的 [[email protected] ~]#rpm -qi aprName                 :apr                                        Relocation

多态方法调用的解析和分派

方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程.在程序运行时,进行方法调用是最普遍.最频繁的操作,Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址(相当于之前说的直接引用).这个特性给Java带来了更强大的动态扩展能力,但也使得Java方法调用过程变得相对复杂起来,需要在类加载期间,甚至到运行期间才能确定目标方法

深入理解Java虚拟机笔记---方法调用

方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程.在程序运行时,进行方法调用是最普遍.最频繁的操作.在Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件里存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址(相当于直接引用).这个特性给Java带来了更强大的动态扩展能力,但也使得Java方法的调用过程变得相对复杂,需要在类加载期间甚至到运行期间才能确定目标方法的直接引用.

关于JVM中方法调用的相关指令,以及解析(Resolution)和分派(Dispatch)的解释——重载的实现原理与重写的实现原理

JVM中相关方法的调用的指令 invokestatic 调用静态方法. invokespecial 用于调用构造器方法<init>.私有方法.父类方法. invokevirtual 用于调用类的所有虚方法. invokeinterface 用于调用接口方法. 解析(resolution)与分派(dispatch) 解析 解析调用一定是个静态的过程,在编译期间就完全确定,在类装载的解析阶段就会把涉及的符号引用全部转变 为可确定的直接引用,不会延迟到运行期再去完成. 下面我们看一段代码: /**

JVM理论:(三/4)方法调用

本文主要总结虚拟机调用方法的过程是怎样的,JAVA虚拟机里面提供了5条方法调用的字节码指令.分别如下: invokestatic:调用静态方法 invokespecial:调用实例构造器<init>方法.私有方法和父类方法. invokevirtual:调用所有的虚方法. invokeinterface:调用接口方法,会在运行时期再确定一个实现此接口的对象. invokedynamic:现在运行时期动态解析出调用点限定符所引用的方法,然后再执行该方法,在此之前的4条指令,分派逻辑都是固化在虚拟