你不了解的Dictionary和ConcurrentDictionary

news2024/10/5 15:04:38

最近在做项目时,多线程中使用Dictionary的全局变量时,发现数据并没有存入到Dictionary中,但是程序也没有报错,经过自己的一番排查,发现Dictionary为非线程安全类型,因此我感觉数据没有写进去的原因是多线程在争夺全局变量时,导致数据未写入,于是去对Dictionary进行仔细的了解。
经过在网上查阅资料,发现大家讲解最多的是Dictionary(非线程安全)和ConcurrentDictionary(线程安全),于是我也从这两个关键字来仔细的讲解,顺便也更加深入的认识它们。
Dictionary怎样解决线程安全问题?可以使用加锁、线程全局变量、使用ConcurrentDictionary等。下面我们就一起来看看吧,Let’s go。

Dictionary

Dictionary<TKey, TValue>泛型类提供了键值对映射,通过TKey来检索值的速度非常快,时间复杂度接近与O(1),是因为Dictionary通过哈希表实现,是一种变相的HashTable,采用分离链接散列表的数据结构解决哈希冲突问题。
在早期的C#版本中,可以将集合初始值设定项用于序列样式集合,包括在键值对周围添加括号而得到Dictionary<TKey, TValue>,如:

Dictionary<int, string> msgs = new Dictionary<int, string>()
{
    { 1, "Hello, "},
    { 2 , "World"},
    { 3, "!"}
};

而新的语法支持使用索引分配到集合中,如:

Dictionary<int, string> MsgErrs = new Dictionary<int, string>()
{
    [1] = "Hello, ",
    [2] = "World",
    [3] = "!",
};

上述两者在初始化赋值时都差不多,但是两者还是有一些区别,前者在初始化时出现重复key值,程序会直接报错。而后者初始化时,key可以有重复值,系统会自动过滤掉重复的key值,程序也不会报错。
实现键/值对集合
每次对字典的添加都包含一个值与其关联的值,通过使用键来检索十分方便;
如果使用集合初始值设定项生成Dictionary集合,可以使用如下方法:

public static Dictionary<string, Element> BuildDic()
        {
 return new Dictionary<string, Element>
            {
                {"L", new Element(){Symbol = "L", Name = "Postass", AutominNumber = 9}},
                {"Q", new Element(){ Symbol = "Q", Name = "Calcium", AutominNumber = 99}},
                {"M", new Element(){ Symbol = "JY", Name = "JYaoiang", AutominNumber=7924}}
            };
        }

Dictionary添加值

public static void IterateDictionary()
        {
            Dictionary<string, Element> element = BuildDic();
 foreach(KeyValuePair<string, Element> keyValue in element)
            {
                Element ele = keyValue.Value;
                Console.WriteLine(string.Format("Key={0}; Values={0};{1};{2}", keyValue.Key, ele.Symbol, ele.Name, ele.AutominNumber));
            }
        }
 public static Dictionary<string, Element> BuildDictionary()
        {
 var elements = new Dictionary<string, Element>();
            AddDictionary(elements, "L", "LLL", 9);
            AddDictionary(elements, "J", "LJLHHH", 19);
            AddDictionary(elements, "A", "ABABABA", 20);
 return elements;
        }
 public static void AddDictionary(Dictionary<string, Element> elements, string symbol, string name, int num)
        {
            Element ele = new Element()
            {
                Symbol = symbol,
                Name = name,
                AutominNumber = num
            };
            elements.Add(key: symbol, value: ele);
        }

ContainsKey方法和Item[]属性

public static void FindDictionary(string symbol)
        {
            Dictionary<string, Element> elements = BuildDictionary();
 if (elements.ContainsKey(symbol))
            {
                Element ele = elements[symbol];
                Console.WriteLine("Found: " + ele.Name);
            }
 else
            {
                Console.WriteLine("Not found " + symbol);
            }
        }

TryGetValue方法

public static void FindDictionaryOfTryGetValue(string symbol)
        {
            Dictionary<string, Element> elements = BuildDictionary();
            Element ele = null;
 if(elements.TryGetValue(symbol, out ele))
            {
                Console.WriteLine("Found: " + ele.Name);
            }
 else
            {
                Console.WriteLine("Not found " + symbol);
            }
        }

在这里讲解了Dictionary的常见使用方法,这里在啰嗦一句,不知道大家在使用Dictionary时有没有注意带Add方法和TryAdd方法,这两个方法到底有什么区别?
我们都知道,在往Dictionary中添加键值时,键是不能重复的,如果使用Add方法添加重复的key,会使程序报错。要想避免这个问题,则可以使用TryAdd方法,当添加重复键值使,该方法会返回false,就可以避免此类问题。

ConcurrentDictionary

.NET Framework 4以及更新的版本中,System.Collections.Concurrent命名空间中的集合可提供高效的线程安全操作,以便从多个线程访问集合项。
当有多个线程访问集合项时,应该使用System.Collections.Concurrent命名空间中的类,而不是使用System.Collections.Generic和System.Collections命名空间中的类。
System.Collections.Concurrent命名空间中的类:BlockingCollection、ConcurrentDictionary<TKey, TValue>、ConcurrentQueue<T>、ConcurrentStack<T>
System.Collections命名空间中的类不会将元素作为特别类型化的对象存储,而是作为object类型的对象存储。
ConcurrentDictionary用法与Dictionary类似,这里就不再详细讲解了。但是ConcurrentDictionary只能使用TryAdd方法,而Dictionary可以使用AddTryAdd方法。

Dictionary和ConcurrentDictionary多线程

带大家认识完DictionaryConcurrentDictionary,下面就回归主题,看看两者在多线程方面的使用情况。
代码如下:

ConcurrentDictionary<int, string> keys = new ConcurrentDictionary<int, string>();
keys.TryAdd(1, "LL");
keys.TryAdd(2, "LL");
Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, "OJ");
dic.TryAdd(2, "R");
Stopwatch stopwatch = new Stopwatch();
#region 写入
stopwatch.Start();
Parallel.For(0, 10000000, i =>
{
 lock (dic)
    {
        dic[i] = new Random().Next(100, 99999).ToString();
    }
});
stopwatch.Stop();
Console.WriteLine("Dictionary加锁写入花费时间:{0}", stopwatch.Elapsed);
stopwatch.Restart();
Parallel.For(0, 10000000, i =>
{
    keys[i] = new Random().Next(100, 99999).ToString();
});
stopwatch.Stop();
Console.WriteLine("ConcurrentDictionary加锁写入花费时间:{0}", stopwatch.Elapsed);
#endregion
#region 读取
string result = string.Empty;
stopwatch.Restart();
Parallel.For(0, 10000000, i =>
{
 lock (dic)
    {
        result = dic[i];
    }
});
stopwatch.Stop();
Console.WriteLine("Dictionary加锁读取花费时间:{0}", stopwatch.Elapsed);
stopwatch.Restart();
Parallel.For(0, 10000000, i =>
{
    result = keys[i];
});
stopwatch.Stop();
Console.WriteLine("ConcurrentDictionary加锁读取花费时间:{0}", stopwatch.Elapsed);
#endregion
Console.ReadLine();

v
​

可以发现,在多线程下,加了lockDictionary写入性能要比ConconcurrentDictionary的写入性能更好,读取数据ConcurrentDictionary性能更好。
当我们将写入的数据增加到20000000时,ConcurrentDictionary写入性能明显就比Dictionary性能差了,但是读取性能ConcurrentDictionary更好。

编辑

当我们将写入的数据增加到2000000时,ConcurrentDictionary写入性能还是比Dictionary性能差,但是读取性能ConcurrentDictionary更好。

编辑

综上,经过对两者的比较,ConcurrentDictionary读取性能更好,Dictionary写入性能更好。
至于,具体是什么原因,到时候我会进行深入讲解,这篇文章大致就讲到这里了,我们下篇文章见。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/860199.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在linux系统上部署Nginx

一、准备环境 1、关闭防火墙 systemctl disable firewalld.service 2、 安装Nginx相关依赖 yum install -y gcc-c zlib zlib-developenssl openssl-devel pcre pcre-devel 二、源码安装 1、上传压缩包并解压到目标文件 cd /usr/local tar -zxvf nginx-1.22.0.tar.gz 2、…

“深入解析Maven:安装、创建项目和依赖管理的完全指南“

目录 引言Maven的安装创建Maven项目之前的装备工作Eclipse创建新的Maven项目项目依赖管理 总结 引言 Maven是一个流行的项目管理工具&#xff0c;被广泛用于Java项目的构建、依赖管理和部署。它提供了一种简单而强大的方式来管理项目的各个方面&#xff0c;使开发人员能够更专…

Node 使用 MySQL

1、安装驱动 使用 npm 进行安装 mysql $ npm install mysql 2、连接数据库 在以下实例中根据你的实际配置修改数据库用户名、及密码及数据库名&#xff1a; test.js 文件 var mysql require(mysql); var connection mysql.createConnection({host : localhost…

这10个在线AI绘图工具太好用了,设计师们快来get!

无论你是一名专业的插画师&#xff0c;还是一个富有创造力、想随时随地记录生活灵感的人&#xff0c;现在只需要拿起平板或打开电脑浏览器&#xff0c;就能将头脑中的画面描绘出来。本篇文章&#xff0c;我们挑选了10款功能强大又方便好用的在线画图软件&#xff0c;其中一定有…

E. Power of Points - 思维

分析&#xff1a; 题意本质就是找点在数组中任意一个位置时和所有的端点之间的距离和&#xff0c;但是直接暴力会超时&#xff0c;可以对数组排个序&#xff0c;设当前遍历的是xi&#xff0c;那么此时求的到各端点的距离就是j从1 ~ i - 1的所有端点与xi的距离之和&#xff0c;也…

07-3_Qt 5.9 C++开发指南_文件目录操作

文章目录 1. 文件目录操作相关的类2. 实例概述2.1 实例功能2.2 信号发射信息的获取 3. QCoreApplication 类4. QFile类5. QFileInfo类6. QDir类7. QTemporaryDir 和QTemporaryFile8. QFileSystemWatcher 类9. 框架和源码9.1 可视化UI设计9.2 dialog.cpp 1. 文件目录操作相关的类…

D. Weights Assignment For Tree Edges - 思维(树)

分析&#xff1a; 给出了父节点&#xff0c;要求到根节点的权重按p的顺序递增&#xff0c; 那么就可以从前往后一个一个赋值&#xff0c;依次加一&#xff0c;当时忽略了树的特性&#xff0c;忽略了节点之间的相互关系&#xff0c;WA了好几次&#xff0c;如果在进行依次递增赋值…

青大数据结构【2015】

一、单选 二、简答 5.如果一组关键字,以不同的次序输入后建立起来的二叉排序树是否相同?当中序遍历这些二叉排序树时,其遍历的结果是否相同?为什么? 不同,因为输入次序不同,所放置的位置与上一个结点有关,次序不同,二叉排序不同; 相同,中序遍历二叉树得到对应的关…

配置nginx服务端口时-在同一个页面中打开多个地址端口-查看服务情况

1&#xff1a;把代码保存到xxx.html文件中 2&#xff1a;因为一个个端口打开查看&#xff0c;实在太麻烦了 3&#xff1a;在一个页面中查看多页的响应才能提高测试效率 <html><head><title>本地连接列表</title> </head><body><cente…

10.Eclipse配置Tomcat详细教程、如何使用Eclipse+tomcat创建并运行web项目

一、Tomcat的下载官网 -> 进入官网显示如图所示的界面&#xff0c;在下下载的是Tomcat9.0版本&#xff0c;你可以自己选一款 点击然后进入下面这个界面 最好是在你的D盘建立一个文件夹&#xff0c;把它解压在里面&#xff0c;文件夹名自己来吧&#xff0c;自己能知道里面装…

MySQL8的特性-MySQL8知识详解

MySQL是一个多用户、多线程的SQL数据库服务器。SQL&#xff08;结构化查询语言&#xff09;是世界上最流行和标准化的数据库语言。下面是MySQL的特性。 1、开源性&#xff1a;MySQL是一个开源的关系型数据库管理系统&#xff0c;可以免费使用和修改。 2、可靠性&#xff1a;M…

网工内推 | 自动化企业招网工,包吃,最高15K,厂商认证优先

01 影儿集团 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、负责公司及分支站点网络架构设计规划、组建、优化及日常运维管理工作&#xff1b; 2、负责公司网络安全、网络质量及网络和安全设备等检查与监控&#xff1b; 3、负责网络设备安全策略的配置及优化&#…

使用Java根据表名导出与导入Sql

前言 很粗糙啊&#xff0c;有很多可以优化的地方&#xff0c;而且也不安全&#xff0c;但是临时用还是OK的&#xff0c;我这个是公司里面的单机软件&#xff0c;不联网。 嗨&#xff01;我是一名社交媒体增长黑客&#xff0c;很高兴能帮助您优化和丰富关于批量作业导出和导入…

openlayers有哪些版本以及区别

vue3openlayer7 openlayer版本介绍 openlayer版本介绍 一、多个项目版本对比 官网首页罗列的几个版本&#xff1a; 包括&#xff1a;v7\v6\v5\v4\v3\v2 两年前使用v6.5.0 2023年7月版本是v7.4.0

CodeForces怎么查找一道题

直接先随便进入一道题的页面&#xff0c;然后改地址栏里面的网址!!! 例如 : 我们要找CF1A这道题的话, 先随便找一道你能看到的题目,如 : 然后将地址栏的158改成1,然后回车就好了

电影院订票选座网站小程序开发(java开源)

搭建一个电影院订票选座网站小程序需要掌握Java语言和相关的Web开发技术&#xff0c;同时需要使用开源框架和库来实现。以下是一个基本的步骤指南&#xff1a; 确定技术栈 首先&#xff0c;需要确定使用的技术栈&#xff0c;以便更好的开展工作。 设计数据库 设计数据库需要…

2023最新Windows编译ffmpeg详细教程,附msys2详细安装配置教程

安装MSYS2 msys2是一款跨平台编译套件&#xff0c;它模拟linux编译环境&#xff0c;支持整合mingw32和mingw64&#xff0c;能很方便的在windows上对一些开源的linux工程进行编译运行。 类似的跨平台编译套件有&#xff1a;msys&#xff0c;cygwin&#xff0c;mingw 优势&…

有人管一管小天才电话手表吗?

作者 | 张未 来源 | 洞见新研社 潮流果然是个圈&#xff0c;曾经风靡2008年的“摇一摇”重回我们的视野当中。 这个对于成年人有些过时的产物&#xff0c;以儿童手表为载体&#xff0c;正入侵着小学生的社交圈&#xff0c;成为儿童的“社交密码”。 “碰一碰”加好友&#x…

如何给a-table增加列宽拖动功能

对于table的列宽设置 相信用过的人都知道&#xff0c;想要设置得很完美&#xff0c;几乎是不现实的&#xff0c;因为总有数据或长或短&#xff0c;那我们应该如何优化它呢&#xff1f;那便是让用户自行拖动列宽&#xff0c;从而能看全table的数据&#xff0c;但是对于antd-vue …

Python进阶-模块和包/random/datetime/ pandas和DataFrame

Python进阶 1. 模块和包 2. random随机数 3. datetime日期模块 4. pandas和DataFrame 1. 模块和包 1.1 模块&#xff1a;就是一个以.py结尾的文件&#xff0c;模块中可以定义函数&#xff0c;类和变量&#xff0c;可执行的代码 Python模块分为&#xff1a;内置模块和第三方模…