DotNet · 2019年10月27日

集合类中Synchronized方法与SyncRoot属性原理分析(转)

我们知道,在.net的一些集合类型中,譬如Hashtable和ArrayList,都有Synchronized静态方法和SyncRoot属性,他们之间有联系吗?我怎么才能用好他们呢?
  以Hashtable为例,看看他们的基本用法:  

1 Hashtable ht = Hashtable.Synchronized(new Hashtable());
2  lock (ht.SyncRoot)
3 {
4 ......
5 }

  Synchronized表示返回一个线程安全的Hashtable,什么样的 hashtable才是一个线程安全的呢?下边我们就从.NET的源码开始理解。

复制代码
1 public static Hashtable Synchronized(Hashtable table)
2 {
3 if (table == null)
4 {
5 throw new ArgumentNullException("table");
6 }
7 return new SyncHashtable(table);
8 }
复制代码

  从源码不难看出,Synchronized方法返回的其实是一个SynchHashtable类型的实例。在前边我们说过,Synchronized表示返回一个线程安全的Hashtable,从这个解释不难看出,SynchHashtable应该是继承自Hashtable。下边我们验证一下。看看 SynchHashtable类型的源码: 

复制代码
1 [Serializable]
2 private class SyncHashtable : Hashtable
3 {
4 // Fields
5 protected Hashtable _table;
6
7 // Methods
8 internal SyncHashtable(Hashtable table)
9 : base(false)
10 {
11 this._table = table;
12 }
13
14 internal SyncHashtable(SerializationInfo info, StreamingContext context)
15 : base(info, context)
16 {
17 this._table = (Hashtable)info.GetValue("ParentTable", typeof(Hashtable));
18 if (this._table == null)
19 {
20 throw new SerializationException(Environment.GetResourceString("Serialization_InsufficientState"));
21 }
22 }
23
24 public override void Add(object key, object value)
25 {
26 lock (this._table.SyncRoot)
27 {
28 this._table.Add(key, value);
29 }
30 }
31
32 public override void Clear()
33 {
34 lock (this._table.SyncRoot)
35 {
36 this._table.Clear();
37 }
38 }
39
40 public override object Clone()
41 {
42 lock (this._table.SyncRoot)
43 {
44 return Hashtable.Synchronized((Hashtable)this._table.Clone());
45 }
46 }
47
48 public override bool Contains(object key)
49 {
50 return this._table.Contains(key);
51 }
52
53 public override bool ContainsKey(object key)
54 {
55 return this._table.ContainsKey(key);
56 }
57
58 public override bool ContainsValue(object key)
59 {
60 lock (this._table.SyncRoot)
61 {
62 return this._table.ContainsValue(key);
63 }
64 }
65
66 public override void CopyTo(Array array, int arrayIndex)
67 {
68 lock (this._table.SyncRoot)
69 {
70 this._table.CopyTo(array, arrayIndex);
71 }
72 }
73
74 public override IDictionaryEnumerator GetEnumerator()
75 {
76 return this._table.GetEnumerator();
77 }
78
79 public override void GetObjectData(SerializationInfo info, StreamingContext context)
80 {
81 if (info == null)
82 {
83 throw new ArgumentNullException("info");
84 }
85 info.AddValue("ParentTable", this._table, typeof(Hashtable));
86 }
87
88 public override void OnDeserialization(object sender)
89 {
90 }
91
92 public override void Remove(object key)
93 {
94 lock (this._table.SyncRoot)
95 {
96 this._table.Remove(key);
97 }
98 }
99
100 internal override KeyValuePairs[] ToKeyValuePairsArray()
101 {
102 return this._table.ToKeyValuePairsArray();
103 }
104
105 // Properties
106 public override int Count
107 {
108 get
109 {
110 return this._table.Count;
111 }
112 }
113
114 public override bool IsFixedSize
115 {
116 get
117 {
118 return this._table.IsFixedSize;
119 }
120 }
121
122 public override bool IsReadOnly
123 {
124 get
125 {
126 return this._table.IsReadOnly;
127 }
128 }
129
130 public override bool IsSynchronized
131 {
132 get
133 {
134 return true;
135 }
136 }
137
138 public override object this[object key]
139 {
140 get
141 {
142 return this._table[key];
143 }
144 set
145 {
146 lock (this._table.SyncRoot)
147 {
148 this._table[key] = value;
149 }
150 }
151 }
152
153 public override ICollection Keys
154 {
155 get
156 {
157 lock (this._table.SyncRoot)
158 {
159 return this._table.Keys;
160 }
161 }
162 }
163
164 public override object SyncRoot
165 {
166 get
167 {
168 return this._table.SyncRoot;
169 }
170 }
171
172 public override ICollection Values
173 {
174 get
175 {
176 lock (this._table.SyncRoot)
177 {
178 return this._table.Values;
179 }
180 }
181 }
182 }
复制代码

  呵呵,果然不出我们所料,SyncHashtable果然继承自Hashtable,SyncHashtable之所以能实现线程的安全操作,就是因为在他们的一些方法中,就加了lock,我们知道,哪一个线程执行了lock操作,在他还没有释放lock之前,其他线程都要处于堵塞状态。 SyncHashtable就是通过这种方法,来实现所谓的线程安全。

  现在我们理解了Synchronized的含义和用法,那接下来我们看看他和SyncRoot之间的关系。
  SyncRoot表示获取可用于同步 Hashtable 访问的对象,老实说,这个解释不好理解,要想真正理解他的用法,我们还得从源码开始:

 

复制代码
1 public virtual object SyncRoot
2 {
3 get
4 {
5 if (this._syncRoot == null)
6 {
7 Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
8 }
9 return this._syncRoot;
10 }
11 }
复制代码

  如果您清楚Interlocked的用法,这段代码没什么难理解的了(不清楚的朋友找GOOGLE吧),Interlocked为多个线程共享的变量提供原子操作。原子操作就是单线程操作。在一个Hashtable实例中,不论我们在代码的任何位置调用SyncRoot,返回的都是同一个object类型的对象。我们在开始写的lock(ht.SyncRoot)和下边的操作作用是一样的:

1 static object obj = new object();
2 lock(obj)
3 {
4 ......
5 }

  他们之间不同的是,我们声明的static object类型对象是类型级别的,而SyncRoot是对象级别的。

  通过上面的分析,我们都应该能理解Synchronized 和 SyncRoot用法,他们之间的关系就是:
  Hashtable 通过Synchronized方法,生成一个SynchHashtable类型的对象,在这个对象的一个方法中,通过lock (this._table.SyncRoot)这样的代码来实现线程安全的操作,其中this._table.SyncRoot返回的就是一个 object类型的对象,在一个SynchHashtable对象实例中,不管我们调用多少次,他是唯一的。

  另外,针对泛型集合的线程安全访问,由于泛型集合中没有直接公布SyncRoot属性,所以猛一看好似无从下手。

  但是查看集合泛型集合的源代码后就可发现他们实际上都提供了SyncRoot属性。

  以下以Queue<T>集合为例。 

复制代码
1 bool ICollection.IsSynchronized
2 {
3 get
4 {
5 return false;
6 }
7 }
8
9 object ICollection.SyncRoot
10 {
11 get
12 {
13 if (this._syncRoot == null)
14 {
15 Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
16 }
17 return this._syncRoot;
18 }
19 }
复制代码

  从以上源代码可以看出,这两个方法都被实现为了显式接口,所以必须将其显式转型为ICollection后才能使用。

1 lock (((ICollection)_queue).SyncRoot)
2 {
3 int item = _queue.Dequeue();
4 }

最新电影,电视剧,尽在午夜剧场

电影电视剧午夜不寂寞