浅解c#多线程读写锁(续)

   上一章我们讨论到了解决方案,本文着重讨论一下该方案的实现.

   一. 排它锁类的实现:

       我们实现一个类TimeSpanWaitor,用它来控制排它锁的获取和释放,其实该类的实现的目的很简单,那就是实现一个方法,该方法接受一个时间片段和一个函数回调(该函数返回bool)的参数,该方法在指定时间片段内不断的轮值排它信号,并在每次获得排它信号时调用传入的函数,当该函数返回True时则返回,否则就一值轮值排它信号直到超时.

      如下:1.轮值排它信号,该过程尝试获取排它信号,如果获取成功则调用回调函数,并根据回调函数返回相应状态.否则返回下一次继续的状态.

             2.如果上一次轮值状态是返回继续则线程随机停顿后循环到1执行,否则退出

具体的代码如下:

View Code
1 public sealed class TimeSpanWaitor
2 {
3
4 public TimeSpanWaitor(int minwaitmillseconds, int maxwaitmillsecondes)
5 {
6 g_AsyncObject = new IntLock();
7 g_DefaultWaitTime = new TimeSpan(0, 0, 1);
8 int min = minwaitmillseconds;
9 if (min < 0)
10 min = 10;
11 int max = maxwaitmillsecondes;
12 if (max < 0)
13 max = 100;
14 if (min > max)
15 {
16 int x = min;
17 min = max;
18 max = x;
19 }
20 if (min == max)
21 {
22 min = 10;
23 max = 100;
24 }
25 g_MaxWaitMillSeconds = max;
26 g_MinWaitMillSeconds = min;
27 g_WaitTimeDom = new Random();
28 }
29
30 public TimeSpanWaitor()
31 : this(DefaultMinWaitTimeMillSeconds, DefaultMaxWaitTimeMillSeconds)
32 {
33 }
34
35 #region 公有常数
36
37 public const int DefaultMaxWaitTimeMillSeconds = 100;
38
39 public const int DefaultMinWaitTimeMillSeconds = 10;
40
41 #endregion
42
43 #region 私有常量
44
45 private IntLock g_AsyncObject;
46
47 private TimeSpan g_DefaultWaitTime;
48
49 private Random g_WaitTimeDom = null;
50
51 private int g_MaxWaitMillSeconds = 0;
52
53 private int g_MinWaitMillSeconds = 0;
54
55 #endregion
56
57 #region 私有方法
58
59 /// <summary>
60 /// 尝试锁定
61 /// </summary>
62 /// <param name="onenter">成功锁定时调用该回调:返回True指示退出获取锁定,否则继续下一次获取锁定</param>
63 /// <returns>尝试结果</returns>
64   private PerWaitEnum TryEnter(Func<bool> onenter)
65 {
66 bool success = g_AsyncObject.Lock();
67 if (success)
68 {
69 PerWaitEnum r = PerWaitEnum.SuccessAndContinue;
70 Exception err = null;
71 try
72 {
73 if (onenter())
74 r = PerWaitEnum.SuccessAndExists;
75 }
76 catch (Exception e)
77 {
78 err = e;
79 }
80 finally
81 {
82 g_AsyncObject.UnLock();
83 }
84 if (err != null)
85 throw err;
86 return r;
87 }
88 return PerWaitEnum.Fail;
89 }
90
91 /// <summary>
92 /// 等待
93 /// </summary>
94 /// <param name="waittimeout">等待超时值</param>
95 /// <param name="dt">上次等待时间</param>
96 /// <returns>返回True指示未超时</returns>
97   private bool WaitTime(ref TimeSpan waittimeout, ref DateTime dt)
98 {
99 if (waittimeout == TimeSpan.MaxValue)
100 {
101 Thread.Sleep(g_WaitTimeDom.Next(g_MinWaitMillSeconds, g_MaxWaitMillSeconds));
102 dt = DateTime.Now;
103 return true;
104 }
105 else if (waittimeout == TimeSpan.MinValue)
106 {
107 dt = DateTime.Now;
108 return false;
109 }
110 else if (waittimeout == TimeSpan.Zero)
111 {
112 dt = DateTime.Now;
113 return false;
114 }
115 else
116 {
117 Thread.Sleep(g_WaitTimeDom.Next(g_MinWaitMillSeconds, g_MaxWaitMillSeconds));
118 waittimeout -= GetNowDateTimeSpan(ref dt);
119 return (waittimeout.Ticks > 0);
120 }
121 }
122 /// <summary>
123 /// 计算此时同tp的时间差,同时tp返回此时时间
124 /// </summary>
125 /// <param name="tp">上次等待时间,返回此时</param>
126 /// <returns>tp同此时的时间差</returns>
127   private TimeSpan GetNowDateTimeSpan(ref DateTime tp)
128 {
129 DateTime kk = tp;
130 tp = DateTime.Now;
131 return tp.Subtract(kk);
132 }
133 #endregion
134
135 #region 公有方法
136
137 /// <summary>
138 /// 等待指定的时间:timeout
139 /// </summary>
140 /// <param name="timeout">等待超时时间:该值=TimeSpan.MaxValue边示无期限的等待</param>
141 /// <param name="onenter">当每次获得等待锁时都调用,返回True表示退出等待,否则再次等待锁,直到超时</param>
142 /// <returns>True表示成功等待到锁并且onenter函数返回True,False:表示等待超时</returns>
143   public bool WaitForTime(TimeSpan timeout, Func<bool> onenter)
144 {
145 TimeSpan tmout = timeout;
146 DateTime n = DateTime.Now;
147 PerWaitEnum r = TryEnter(onenter);
148 while (r != PerWaitEnum.SuccessAndExists)
149 {
150 if (!WaitTime(ref tmout, ref n))
151 break;
152 r = TryEnter(onenter);
153 }
154 return r == PerWaitEnum.SuccessAndExists;
155 }
156 #endregion
157 }
158 internal enum PerWaitEnum
159 {
160 SuccessAndExists,
161 SuccessAndContinue,
162 Fail
163 }

bool WaitForTime(TimeSpan timeout, Func<bool> onenter)方法为实现的方法,该方法timeout参数为超时值,参数onenter为一个返回bool的函数回调,它是在每次获取了排它信号时调用的,调用方可以在此回调内执行上一章解决方案中的步骤二:判断读写锁逻辑是否满足,如果满足则进行锁登记等等操作.该回调返回true则说明不必再去轮值排它锁了,也就是说调用方已经知道读写锁是否可以获取了.返回false说明本次轮值还不能确定是否可以获取,应该继续轮值.

其实该方法就如同如下函义:

   lock(obj)

{

   onenter();

} 不同之处在于,它是不断的轮值调用,直到调用方返回True或超时.调用方在onenter方法内时明白此时的所有数据都是同步的.

   正如前面说的一样,WaitForTime方法分两个主要步骤,而其内部方法TryEnter则实现了步骤一,该方法根据是否取得排它锁及取得后回调函数的返回情况分别返回PerWaitEnum.SuccessAndContinue(继续),PerWaitEnum.SuccessAndExists(退出),PerWaitEnum.Fail(排它锁获取失败)三种状态, 而主函数WaitForTime则根据这三种状态判断是成功退出还是继续循环轮值还是失败返回,其中在每次循环前会调用WaitTime随机停顿一断时间.该类的排它信号采用了上一章介绍的IntLock类.这个类的名称有网友说有点不好,是的,这个名称确实不妥.

  最后看看整个读写锁的主函数:

读写锁获取的主函数
1 public IDisposeState LockRead(TimeSpan timeout,Func<bool> isvalidstate)
2 {
3 IDisposeState rstate = null;
4 Func<bool> f = () =>
5 {
6 if (g_Disposed)
7 {
8 rstate = DisposedState.Empty;
9 return true;
10 }
11 if (TryLockRead())
12 {
13 bool isvalid = isvalidstate != null ? isvalidstate() : true;
14 if (!isvalid)
15 rstate = DisposedState.Empty;
16 else
17 rstate =
18 MutilThreadDisposeStatePools.GetMutilThreadDisposeState(
19 true, false, this);
20 return true;
21 }
22 else
23 {
24 return false;
25 }
26 };
27 if (g_Lock.WaitForTime(timeout, f))
28 return rstate;
29 else
30 return DisposedState.Empty;
31 }
32
33 public IDisposeState LockWrite(TimeSpan timeout, Func<bool> isvalidstate)
34 {
35 IDisposeState rstate = null;
36 Func<bool> f = () =>
37 {
38 if (g_Disposed)
39 {
40 rstate = DisposedState.Empty;
41 return true;
42 }
43 if (TryLockWrite())
44 {
45 bool isvalid = isvalidstate != null ? isvalidstate() : true;
46 if (isvalid)
47 rstate =
48 MutilThreadDisposeStatePools.GetMutilThreadDisposeState(
49 isvalid, true, this);
50 else
51 rstate = DisposedState.Empty;
52 return true;
53 }
54 else
55 {
56 return false;
57 }
58 };
59 if (g_Lock.WaitForTime(timeout, f))
60 return rstate;
61 else
62 return DisposedState.Empty;
63 }

这两个函数的结构一致,只是一个是用读锁逻辑(TryLockRead)判断,一个是用写(TryLockWrite)锁逻辑判断,它们的判断逻辑都在定义的函数体f内执行,执行函数体f时,此时类内部的数据是不会有其它线程改变的.并且在g_Lock.WaitForTime函数内会不断调用函数体f的,直到超时获函数体f返回true.而读写锁逻辑的实现我就不说了,这个比较好看明白.

   对于多线程读写锁的一些讨论就先到此了,完整的类见博文:http://www.cnblogs.com/ren700622/archive/2011/05/23/2054455.html

posted on 2011-05-29 17:04  悠竹客  阅读(2115)  评论(0编辑  收藏  举报

导航