Skip to content

Commit 4378a7c

Browse files
committed
完善线程安全的同步字典(SynchronizedDictionary)的单元测试。 🐚
1 parent dbcb34c commit 4378a7c

File tree

1 file changed

+220
-0
lines changed

1 file changed

+220
-0
lines changed

Zongsoft.Core/test/Collections/SynchronizedDictionaryTest.cs

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,170 @@ await Parallel.ForAsync(0, COUNT, (index, _) =>
135135
});
136136
}
137137

138+
[Fact]
139+
public void GetOrAdd()
140+
{
141+
const int COUNT = 1_0000;
142+
143+
var keys = System.Linq.Enumerable.Range(0, COUNT).ToArray();
144+
Random.Shared.Shuffle(keys);
145+
146+
var dictionary = new SynchronizedDictionary<int, string>(COUNT);
147+
for(int i = 0; i < COUNT; i++)
148+
{
149+
if(i % 2 == 1)
150+
dictionary[i] = $"Value#{i}";
151+
}
152+
153+
Assert.Equal(COUNT / 2, dictionary.Count);
154+
155+
Parallel.For(0, COUNT, index =>
156+
{
157+
var key = keys[index];
158+
var value = dictionary.GetOrAdd(key, key => $"Added#{key}");
159+
160+
if(key % 2 == 0)
161+
Assert.Equal($"Added#{key}", value);
162+
else
163+
Assert.Equal($"Value#{key}", value);
164+
165+
if(index % 100 == 0)
166+
{
167+
foreach(var item in dictionary)
168+
{
169+
Assert.True(item.Key >= 0);
170+
Assert.True(dictionary.ContainsKey(item.Key));
171+
}
172+
}
173+
});
174+
175+
Assert.Equal(COUNT, dictionary.Count);
176+
}
177+
178+
[Fact]
179+
public async Task GetOrAddAsync()
180+
{
181+
const int COUNT = 1_0000;
182+
183+
var keys = System.Linq.Enumerable.Range(0, COUNT).ToArray();
184+
Random.Shared.Shuffle(keys);
185+
186+
var dictionary = new SynchronizedDictionary<int, string>(COUNT);
187+
for(int i = 0; i < COUNT; i++)
188+
{
189+
if(i % 2 == 1)
190+
dictionary[i] = $"Value#{i}";
191+
}
192+
193+
Assert.Equal(COUNT / 2, dictionary.Count);
194+
195+
await Parallel.ForAsync(0, COUNT, (index, _) =>
196+
{
197+
var key = keys[index];
198+
var value = dictionary.GetOrAdd(key, key => $"Added#{key}");
199+
200+
if(key % 2 == 0)
201+
Assert.Equal($"Added#{key}", value);
202+
else
203+
Assert.Equal($"Value#{key}", value);
204+
205+
if(index % 100 == 0)
206+
{
207+
foreach(var item in dictionary)
208+
{
209+
Assert.True(item.Key >= 0);
210+
Assert.True(dictionary.ContainsKey(item.Key));
211+
}
212+
}
213+
214+
return ValueTask.CompletedTask;
215+
});
216+
217+
Assert.Equal(COUNT, dictionary.Count);
218+
}
219+
220+
[Fact]
221+
public void AddOrUpdate()
222+
{
223+
const int COUNT = 1_0000;
224+
225+
var keys = System.Linq.Enumerable.Range(0, COUNT).ToArray();
226+
Random.Shared.Shuffle(keys);
227+
228+
var dictionary = new SynchronizedDictionary<int, string>(COUNT);
229+
for(int i = 0; i < COUNT; i++)
230+
{
231+
if(i % 2 == 1)
232+
dictionary[i] = $"Value#{i}";
233+
}
234+
235+
Assert.Equal(COUNT / 2, dictionary.Count);
236+
237+
Parallel.For(0, COUNT, index =>
238+
{
239+
var key = keys[index];
240+
var value = dictionary.AddOrUpdate(key, key => $"Added#{key}", (key, value) => $"Updated#{key}");
241+
242+
if(key % 2 == 0)
243+
Assert.Equal($"Added#{key}", value);
244+
else
245+
Assert.Equal($"Updated#{key}", value);
246+
247+
if(index % 100 == 0)
248+
{
249+
foreach(var item in dictionary)
250+
{
251+
Assert.True(item.Key >= 0);
252+
Assert.True(dictionary.ContainsKey(item.Key));
253+
}
254+
}
255+
});
256+
257+
Assert.Equal(COUNT, dictionary.Count);
258+
}
259+
260+
[Fact]
261+
public async Task AddOrUpdateAsync()
262+
{
263+
const int COUNT = 1_0000;
264+
265+
var keys = System.Linq.Enumerable.Range(0, COUNT).ToArray();
266+
Random.Shared.Shuffle(keys);
267+
268+
var dictionary = new SynchronizedDictionary<int, string>(COUNT);
269+
for(int i = 0; i < COUNT; i++)
270+
{
271+
if(i % 2 == 1)
272+
dictionary[i] = $"Value#{i}";
273+
}
274+
275+
Assert.Equal(COUNT / 2, dictionary.Count);
276+
277+
await Parallel.ForAsync(0, COUNT, (index, _) =>
278+
{
279+
var key = keys[index];
280+
var value = dictionary.AddOrUpdate(key, key => $"Added#{key}", (key, value) => $"Updated#{key}");
281+
282+
if(key % 2 == 0)
283+
Assert.Equal($"Added#{key}", value);
284+
else
285+
Assert.Equal($"Updated#{key}", value);
286+
287+
if(index % 100 == 0)
288+
{
289+
foreach(var item in dictionary)
290+
{
291+
Assert.True(item.Key >= 0);
292+
Assert.True(dictionary.ContainsKey(item.Key));
293+
}
294+
}
295+
296+
return ValueTask.CompletedTask;
297+
});
298+
299+
Assert.Equal(COUNT, dictionary.Count);
300+
}
301+
138302
[Fact]
139303
public void Remove()
140304
{
@@ -200,4 +364,60 @@ await Parallel.ForAsync(0, COUNT, (index, _) =>
200364

201365
Assert.Empty(dictionary);
202366
}
367+
368+
[Fact]
369+
public async Task ChaosAsync()
370+
{
371+
const int COUNT = 1_0000;
372+
373+
var dictionary = new SynchronizedDictionary<int, string>(COUNT);
374+
var tasks = new Task[]
375+
{
376+
Task.Factory.StartNew(Run, dictionary),
377+
Task.Factory.StartNew(Run, dictionary),
378+
Task.Factory.StartNew(Run, dictionary),
379+
Task.Factory.StartNew(Run, dictionary),
380+
Task.Factory.StartNew(Run, dictionary),
381+
};
382+
383+
await Task.WhenAll(tasks);
384+
Assert.Equal(COUNT, dictionary.Count);
385+
386+
static void Run(object state)
387+
{
388+
var index = 0;
389+
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
390+
var dictionary = (SynchronizedDictionary<int, string>)state;
391+
392+
while(Hit(dictionary, index, stopwatch.Elapsed) < COUNT)
393+
index = index >= COUNT ? 0 : index + 1;
394+
395+
stopwatch.Stop();
396+
}
397+
398+
static int Hit(SynchronizedDictionary<int, string> dictionary, int key, TimeSpan duration)
399+
{
400+
switch(Random.Shared.Next() % 10)
401+
{
402+
case 0:
403+
if(duration.TotalSeconds < 1.0)
404+
dictionary.Remove(key);
405+
break;
406+
case 1:
407+
case 2:
408+
dictionary.TryAdd(key, $"Value#{key}");
409+
break;
410+
case 3:
411+
case 4:
412+
if(!dictionary.TryUpdate(key, $"Value.{key}"))
413+
dictionary.TryAdd(key, $"Value#{key}");
414+
break;
415+
default:
416+
dictionary.AddOrUpdate(key, key => $"Added:{key}", (key, value) => $"Updated:{key}");
417+
break;
418+
}
419+
420+
return dictionary.Count;
421+
}
422+
}
203423
}

0 commit comments

Comments
 (0)