争用指多个线程试图同时获取一个锁;高争用意味着存在大量正在争用的线程;低争用的意思与高争用相反。在TTASLock类中,setlock()方法使用了两个步骤:它不断地读锁,当锁看似空闲时,则尝试获得锁。如果其他的某个线程在第一步和第二步之间获得了锁,那么该锁极有可能存在高争用。显然,试图获得一个存在高争用的锁是一种应该回避的情形。此时线程获得锁的机会非常小,因为这种尝试将会导致总线流量的增加(导致流量拥塞)。相反,若让线程后退一段时间,给正在竞争的线程以结束的机会,将会更加有效。
线程在重试之前应该后退多久呢?一种好的准则就是不成功尝试的次数越多,发生争用的可能性就越高,线程需要后退的时间就应越长。下面是一种简单的方法,每当线程发现锁变为空闲但却无法获得它时,就在重试之前后退。为了确保发生冲突的并发线程不进入获取锁的步骤,即在同一时刻所有线程都试图获得锁,该线程应随机地后退一段时间。每当线程试图获得一个锁但又失败以后,则将后退时间加倍,直到一个固定的最大值maxDelay为止。
Code
1 public class Backoff
2 {
3 private int minDelay, maxDelay;
4 private int limit;
5 private Random random;
6
7 public Backoff(int min, int max)
8 {
9 minDelay = min;
10 maxDelay = max;
11 limit = minDelay;
12 random = new Random();
13 }
14
15 public void backoff()
16 {
17 int delay = random.Next(limit);
18 limit = Math.Min(maxDelay, 2 * limit);
19 Thread.Sleep(delay);
20 }
21 }
22
23 public class BackoffLock : ILock
24 {
25 private const int WAITING = 0;
26 private const int EXECUTING = 1;
27 int state = WAITING;
28
29 private static readonly int MIN_DELAY = 100;
30 private static readonly int MAX_DELAY = 1000;
31
32 public void setLock()
33 {
34 Backoff backoff = new Backoff(MIN_DELAY, MAX_DELAY);
35 while (true)
36 {
37 while (state == EXECUTING) { }
38 if (Interlocked.CompareExchange(ref state, EXECUTING, WAITING) == WAITING)
39 {
40 return;
41 }
42 else
43 {
44 backoff.backoff();
45 }
46 }
47 }
48
49 public void unlock()
50 {
51 Interlocked.Exchange(ref state, WAITING);
52 }
53 }