c#中一个多线程安全的HashSet

封装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);
}
相关推荐
秋名RG1 小时前
Java 异常处理全攻略:从入门到实战(JDK 21 版)
java·开发语言
努力努力再努力wz1 小时前
【Docker入门系列】:从架构演进到容器化:一文建立 Docker、虚拟化与 Namespace 的底层心智模型
运维·开发语言·数据结构·c++·docker·容器·架构
wuyk5551 小时前
13.堆排序:基于完全二叉树的高效排序算法一、什么是堆排序?
开发语言·算法·排序算法
runningshark1 小时前
Lecture: The ‘Why & How‘ Principle: Moving Beyond Simple Statements
开发语言·前端·javascript
学逆向的1 小时前
PE——RVA与FOA的转换
开发语言·网络安全·pe
zz-zjx1 小时前
Python实用转换模板
开发语言·前端·python
zuozong_2 小时前
C++类与对象
开发语言·c++·算法
lfSeanDragon2 小时前
数据结构-前缀树(Trie)
开发语言·c#
励志不掉头发的内向程序员2 小时前
【LibreCAD 2D架构】从两个坐标到图形实体:RS_ActionDrawLine如何创建RS_Line
开发语言·c++·qt·学习·系统架构