封装ThreadSafeHashSet.cs
csharp
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace LockTest
{
public class ThreadSafeHashSet<T> : IEnumerable<T>
{
private readonly ConcurrentDictionary<T, byte> _set = new();
/// <summary>
/// 添加元素;已存在则无效果
/// </summary>
public void Add(T item)
{
_set.TryAdd(item, 0);
}
/// <summary>
/// 原子添加:不存在则添加,返回true代表本次成功新增;false代表元素已存在
/// </summary>
public bool TryAdd(T item)
{
return _set.TryAdd(item, 0);
}
public bool Remove(T item)
{
return _set.TryRemove(item, out _);
}
public bool Contains(T item)
{
return _set.ContainsKey(item);
}
public void Clear()
{
_set.Clear();
}
public int Count => _set.Count;
public bool IsEmpty => _set.IsEmpty;
public IEnumerator<T> GetEnumerator()
{
// 返回key集合迭代器;注意:迭代时集合可能被其他线程修改,快照不一致属于Concurrent集合正常行为
return _set.Keys.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
测试
csharp
using System;
using System.Collections.Generic;
using System.Text;
namespace LockTest
{
public class Class1
{
public static async Task Test1()
{
var safeSet = new ThreadSafeHashSet<int>();
int taskCount = 5;
int loopPerTask = 2000;
// ========== 多线程并发写入 ==========
Task[] writeTasks = new Task[taskCount];
for (int i = 0; i < taskCount; i++)
{
int threadId = i;
writeTasks[i] = Task.Run(() =>
{
for (int j = 0; j < loopPerTask; j++)
{
int val = threadId * 10000 + j;
bool ok = safeSet.TryAdd(val);
// ok=true 真正新增;false 重复
}
});
}
// ========== 多线程并发删除 ==========
Task[] removeTasks = new Task[3];
for (int i = 0; i < removeTasks.Length; i++)
{
removeTasks[i] = Task.Run(() =>
{
Random rand = new Random(Guid.NewGuid().GetHashCode());
for (int j = 0; j < 800; j++)
{
int randomVal = rand.Next(0, 50000);
safeSet.Remove(randomVal);
}
});
}
// ========== 多线程并发查询/遍历 ==========
Task[] readTasks = new Task[2];
for (int i = 0; i < readTasks.Length; i++)
{
readTasks[i] = Task.Run(() =>
{
for (int j = 0; j < 1000; j++)
{
bool exist = safeSet.Contains(1234);
// 遍历,注意:遍历过程集合会变化,属于正常现象
foreach (var item in safeSet)
{
// do nothing
}
}
});
}
// 等待全部任务完成
Task.WaitAll(writeTasks);
Task.WaitAll(removeTasks);
Task.WaitAll(readTasks);
Console.WriteLine($"集合最终数量:{safeSet.Count}");
Console.WriteLine("===== 测试 TryAdd 原子性演示 =====");
bool r1 = safeSet.TryAdd(99999);
bool r2 = safeSet.TryAdd(99999);
Console.WriteLine($"第一次Add 99999:{r1}");
Console.WriteLine($"第二次Add 99999:{r2}");
Console.WriteLine($"Contains 99999:{safeSet.Contains(99999)}");
Console.WriteLine($"Remove 99999:{safeSet.Remove(99999)}");
Console.WriteLine($"Contains 99999:{safeSet.Contains(99999)}");
Console.ReadLine();
}
}
}
使用
csharp
var safeSet = new ThreadSafeHashSet<string>();
safeSet.Add("A");
bool newOk = safeSet.TryAdd("A"); // false,已存在
Console.WriteLine(safeSet.Contains("A")); // true
safeSet.Remove("A");
Console.WriteLine(safeSet.Count); //0
safeSet.Add("X");
safeSet.Add("Y");
foreach (var s in safeSet)
{
Console.WriteLine(s);
}