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);
}
相关推荐
小羊没烦恼!5 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
神仙别闹5 天前
基于C#+MySQL实现(WinForm)个人聊天室软件
c#
伞伞悦读5 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
C语言小火车5 天前
C/C++ 为什么需要编译器?
开发语言·c++
霍霍的袁5 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
kybs19915 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net
孙启超5 天前
【AI开发之Rust】第 11 课:智能指针与内部可变性
开发语言·后端·rust
此生决int5 天前
深入理解C++系列(20)——C++11(下)
开发语言·c++
CoderYanger5 天前
A.每日一题:835. 图像重叠
java·开发语言·程序人生·leetcode·面试·职场和发展·学习方法
伞伞悦读5 天前
【第37期】Python JSON 与配置详解:序列化、反序列化、嵌套结构和配置文件
开发语言·python·json