Lock-free并发Stack的数组实现
关于lock-free技术和并发数据结构可以参考花开花落的大作,其中给出了一个基于链表实现的lock-free并发Stack,本文研究lock-free并发stack的数组实现。
在非并发Stack的实现上,一般认为,利用数组要比链表节省时间,同时浪费空间。然而,笔者认为,在并发Stack中,数组在时间上的优势可能不复存在。原因是:
- 链表实现中,效率瓶颈在top节点指针,必须通过CAS原语对该指针进行锁定。
- 数组实现中,效率瓶颈除了top节点索引外,还要考虑Stack扩容时数组复制带来的冲突。
在笔者的实现方案中(代码如下所示),凡是判断当前Stack已满的线程都要进行数组扩容复制,,最终利用CAS原语只有一组扩容结果会被更新到原Stack上,这就需要保证从开始进行扩容复制到更新原Stack这段时间,不发生任何其他对原Stack的更新,否则就会产生更新丢失。因此,有必要保证从扩容复制到原Stack更新操作的整个过程是原子操作。这将花费不小的代价。
代码如下:
1
using System;
2
using System.Threading;
3
using System.Collections;
4
using System.Collections.Generic;
5
6
namespace UCMore
7
{
8
/// <summary>
9
/// Concurrency Stack for multi-thread enviroment.
10
/// </summary>
11
/// <typeparam name="T">Specifices the type of elements in the <see cref="UCMStack"/>.</typeparam>
12
public class UCMStack<T> : IEnumerable
13
{
14
private T[] array;
15
private int top = -1;
16
private int capacity = 0;
17
private int defaultCapacity = 2;
18
19
/// <summary>
20
/// Intializes a <see cref="UCMStack"/> instance.
21
/// </summary>
22
public UCMStack()
23
{
24
}
25
26
27
/// <summary>
28
/// Initializes a <see cref="UCMStack"/> instance, specifing a initial capacity for it.
29
/// </summary>
30
/// <param name="initialCapacity">The initial capacity.</param>
31
public UCMStack(int initialCapacity)
32
{
33
if (initialCapacity < 0)
34
{
35
throw new ArgumentOutOfRangeException("Capacity cannot be negative.");
36
}
37
array = new T[initialCapacity];
38
39
capacity = initialCapacity;
40
}
41
42
/// <summary>
43
/// Determints whether the <see cref="UCMStack"/> is empty.
44
/// </summary>
45
/// <returns> True represents the <see cref="UCMStack"/> is empty, while
46
/// false represents it is not empty.</returns>
47
public bool IsEmpty()
48
{
49
return top == -1;
50
}
51
52
/// <summary>
53
/// Gets the number of elements contained in the <see cref="UCMStack"/>.
54
/// </summary>
55
public int Count
56
{
57
get
58
{
59
return top + 1;
60
}
61
}
62
63
//TODO: Lock-free UCMStack<T>.Push
64
/// <summary>
65
/// Inserts an object at the top of the <see cref="UCMStack"/>.
66
/// </summary>
67
/// <param name="item">An object to be instered.</param>
68
public void Push(T item)
69
{
70
int oldCapacity;
71
int oldTop;
72
73
if (this.Count == (oldCapacity = capacity))
74
{
75
//oldCapcaity = capacity;
76
T[] newArray = new T[this.Count == 0 ? defaultCapacity : oldCapacity * 2];
77
if (this.Count != 0) Array.Copy(array, newArray, this.Count);
78
79
if(Interlocked.CompareExchange(
80
ref capacity,
81
this.Count == 0 ? defaultCapacity : capacity * 2,
82
oldCapacity) == oldCapacity)
83
{
84
array = newArray;
85
}
86
}
87
88
do
89
{
90
oldTop = top;
91
}
92
while (Interlocked.CompareExchange(ref top, oldTop + 1, oldTop) != oldTop);
93
94
array[oldTop + 1] = item;
95
}
96
97
//TODO: Test UCMStack<T>.Pop
98
/// <summary>
99
/// Removes and returns the object at the top of the <see cref="UCMStack"/>.
100
/// </summary>
101
/// <exception cref="InvalidOperationException">Thrown if the <see cref="UCMStack"/> is empty.</exception>
102
/// <returns>The object at the top of the <see cref="UCMStack"/>.</returns>
103
public T Pop()
104
{
105
int oldTop;
106
T popped;
107
108
if (this.IsEmpty())
109
{
110
throw new InvalidOperationException("The Stack is empty.");
111
}
112
113
do
114
{
115
oldTop = top;
116
popped = array[top];
117
}
118
while (Interlocked.CompareExchange(ref top, oldTop - 1, oldTop) != oldTop);
119
120
array[oldTop] = default(T);
121
122
return popped;
123
}
124
125
/// <summary>
126
/// Returns the object at the top of the <see cref="UCMStack"/>, but don't Removes it.
127
/// </summary>
128
/// <exception cref="InvalidOperationException">Thrown if the <see cref="UCMStack"/> is empty.</exception>
129
/// <returns>The object at the top of the <see cref="UCMStack"/>.</returns>
130
public T Peek()
131
{
132
if (this.IsEmpty())
133
{
134
throw new InvalidOperationException("The Stack is empty.");
135
}
136
137
return array[top];
138
}
139
140
/// <summary>
141
/// Removes all the elements from the <see cref="UCMStack"/>.
142
/// </summary>
143
public void Clear()
144
{
145
int oldTop;
146
while (top >= 0)
147
{
148
do
149
{
150
oldTop = top;
151
}
152
while (Interlocked.CompareExchange(ref top, oldTop - 1, oldTop) != oldTop);
153
154
array[oldTop] = default(T);
155
}
156
}
157
158
IEnumerable Members
173
}
174
}
using System;2
using System.Threading;3
using System.Collections;4
using System.Collections.Generic;5

6
namespace UCMore7
{8
/// <summary>9
/// Concurrency Stack for multi-thread enviroment.10
/// </summary>11
/// <typeparam name="T">Specifices the type of elements in the <see cref="UCMStack"/>.</typeparam>12
public class UCMStack<T> : IEnumerable13
{14
private T[] array;15
private int top = -1;16
private int capacity = 0;17
private int defaultCapacity = 2;18

19
/// <summary>20
/// Intializes a <see cref="UCMStack"/> instance.21
/// </summary>22
public UCMStack()23
{24
}25

26

27
/// <summary>28
/// Initializes a <see cref="UCMStack"/> instance, specifing a initial capacity for it.29
/// </summary>30
/// <param name="initialCapacity">The initial capacity.</param>31
public UCMStack(int initialCapacity)32
{33
if (initialCapacity < 0)34
{35
throw new ArgumentOutOfRangeException("Capacity cannot be negative.");36
}37
array = new T[initialCapacity];38

39
capacity = initialCapacity;40
}41

42
/// <summary>43
/// Determints whether the <see cref="UCMStack"/> is empty.44
/// </summary>45
/// <returns> True represents the <see cref="UCMStack"/> is empty, while 46
/// false represents it is not empty.</returns>47
public bool IsEmpty()48
{49
return top == -1;50
}51

52
/// <summary>53
/// Gets the number of elements contained in the <see cref="UCMStack"/>.54
/// </summary>55
public int Count56
{57
get 58
{ 59
return top + 1; 60
}61
}62

63
//TODO: Lock-free UCMStack<T>.Push64
/// <summary>65
/// Inserts an object at the top of the <see cref="UCMStack"/>.66
/// </summary>67
/// <param name="item">An object to be instered.</param>68
public void Push(T item)69
{70
int oldCapacity;71
int oldTop;72

73
if (this.Count == (oldCapacity = capacity))74
{75
//oldCapcaity = capacity;76
T[] newArray = new T[this.Count == 0 ? defaultCapacity : oldCapacity * 2];77
if (this.Count != 0) Array.Copy(array, newArray, this.Count);78

79
if(Interlocked.CompareExchange(80
ref capacity,81
this.Count == 0 ? defaultCapacity : capacity * 2,82
oldCapacity) == oldCapacity)83
{84
array = newArray;85
}86
}87

88
do89
{90
oldTop = top;91
}92
while (Interlocked.CompareExchange(ref top, oldTop + 1, oldTop) != oldTop);93

94
array[oldTop + 1] = item;95
}96

97
//TODO: Test UCMStack<T>.Pop98
/// <summary>99
/// Removes and returns the object at the top of the <see cref="UCMStack"/>.100
/// </summary>101
/// <exception cref="InvalidOperationException">Thrown if the <see cref="UCMStack"/> is empty.</exception>102
/// <returns>The object at the top of the <see cref="UCMStack"/>.</returns>103
public T Pop()104
{105
int oldTop;106
T popped;107

108
if (this.IsEmpty())109
{110
throw new InvalidOperationException("The Stack is empty.");111
}112
113
do114
{115
oldTop = top;116
popped = array[top];117
}118
while (Interlocked.CompareExchange(ref top, oldTop - 1, oldTop) != oldTop);119
120
array[oldTop] = default(T);121

122
return popped; 123
}124

125
/// <summary>126
/// Returns the object at the top of the <see cref="UCMStack"/>, but don't Removes it. 127
/// </summary>128
/// <exception cref="InvalidOperationException">Thrown if the <see cref="UCMStack"/> is empty.</exception>129
/// <returns>The object at the top of the <see cref="UCMStack"/>.</returns>130
public T Peek()131
{132
if (this.IsEmpty())133
{134
throw new InvalidOperationException("The Stack is empty.");135
}136

137
return array[top];138
}139

140
/// <summary>141
/// Removes all the elements from the <see cref="UCMStack"/>.142
/// </summary>143
public void Clear()144
{145
int oldTop;146
while (top >= 0)147
{148
do149
{150
oldTop = top;151
}152
while (Interlocked.CompareExchange(ref top, oldTop - 1, oldTop) != oldTop);153

154
array[oldTop] = default(T);155
}156
}157

158
IEnumerable Members173
}174
}


浙公网安备 33010602011771号