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);
}
相关推荐
xiaoye-duck7 小时前
《C++ 项目:手写大模型接入 SDK》Gemini接口设计(上):初始化、全量响应实现和测试
开发语言·网络·c++·语言模型
nice_lcj52010 小时前
Java Lambda 表达式入门:怎么写、怎么用、常用方法有哪些
java·开发语言·后端·面试
码农小河6610 小时前
基于SSM+JSP+MySQL的在线医疗服务系统的设计与实现
java·开发语言·mysql
nice_lcj52014 小时前
ThreadLocal 入门:它是怎么隔离数据的、为什么会导致内存泄漏
java·开发语言·后端·面试·并发编程·内存泄漏·threadlocal
紧跟先前的步伐14 小时前
Go AI Agent 实战 28|Agent 的预算、超时与停止条件
开发语言·人工智能·后端·ai·golang
bksczm16 小时前
InnoDB 行格式详解:DYNAMIC / COMPACT / COMPRESSED / REDUNDANT
java·开发语言·数据库
春涧草茶16 小时前
慢就是快16-1协程
开发语言·前端·python
Dovis(誓平步青云)16 小时前
启元 T1 Pro 人机相声搭档:语音-触摸双通道的启元机器人节奏引擎设计
开发语言·前端·javascript·microsoft·交互
做科研的周师兄16 小时前
【R语言实战】MixSIAR 氢氧同位素水源解析全流程:从混合空间校验到贝叶斯 MCMC 收敛诊断(后附完整代码)
java·大数据·开发语言
.痞老板.18 小时前
java ee--计算机是如何工作的
java·开发语言·java-ee