首页 > 其他分享 >dotnet 警惕 ConcurrentDictionary 使用 FirstOrDefault 获取到非预期的首项

dotnet 警惕 ConcurrentDictionary 使用 FirstOrDefault 获取到非预期的首项

时间:2023-09-05 15:01:50浏览次数:40  
标签:FirstOrDefault git 里面 ConcurrentDictionary 获取 dotnet 字典

在 dotnet 里面的 ConcurrentDictionary 是一个支持并发读写的线程安全字典,在这个字典里面有一些行为会出现随机性,即多次执行相同的代码返回的结果可能不相同。本文记录在 ConcurrentDictionary 使用 FirstOrDefault 获取到非预期的首项的问题

在 dotnet 里面,无论是对 List 列表,还是 Dictionary 字典等获取首项,使用 FirstOrDefault 总是可以获取到第一个加入到集合或字典里面的元素。然而这个行为在 ConcurrentDictionary 里面是不成立的。在 ConcurrentDictionary 里面如果使用 FirstOrDefault 方法,则随机获取到字典里面的一项,但对相同的一个 ConcurrentDictionary 对象多次调用 FirstOrDefault 方法,在不更改 ConcurrentDictionary 内容的情况下,可以稳定获取到相同的首项元素对象

简单来说就是在 ConcurrentDictionary 里面,调用 FirstOrDefault 方法,不能保证获取到的对象就是第一个加入到 ConcurrentDictionary 字典里面的对象

如以下代码例子

using System.Collections.Concurrent;

for (int i = 0; i < int.MaxValue; i++)
{
    var dictionary = new ConcurrentDictionary<Foo, int>();
    dictionary.TryAdd(new Foo(), i);
    dictionary.TryAdd(new Foo(), i + 1);

    var first = dictionary.FirstOrDefault();
    if (first.Value != i)
    {
        // 证明首个不是第一个加入的
        Console.WriteLine($"首个不是第一个加入的");
        return;
    }
}

class Foo
{
    public Foo()
    {
        Number = _count;
        _count++;
    }

    private static int _count;

    public int Number { get; }
}

以上代码在一个大的循环里面,每次循环都创建一个字典,在给字典加入两个元素,最后加入的元素设置为和循环次数不相同的值,通过此可以用来在后续调用 FirstOrDefault 时判断获取到的元素是否首个加入字典的元素

运行代码可以看到,使用 FirstOrDefault 获取到的元素,不是第一个加入字典的元素。同时,多次运行代码,可以看到进入 if (first.Value != i) 不等于条件时的循环次数也会不相同,这就可以证明使用 FirstOrDefault 的执行结果比较随机

具体原理是在 ConcurrentDictionary 里面需要维护一个 Table 字典,字典里面存放的顺序和传入的 Key 对象的 Hash 有关,调用 FirstOrDefault 方法时获取到的是里面的 Table 字典的按照内存空间顺序的首项

由此原理即可知道,使用 FirstOrDefault 获取 ConcurrentDictionary 的首现是无法确保获取到的是首个加入字典的元素对象。同时如果在 ConcurrentDictionary 字典发生变更,比如不断加入值时,将导致调用 FirstOrDefault 无法稳定返回相同的对象

本文的代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 623c123b1d5e5669c5321c846d72c09e042135a6

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 623c123b1d5e5669c5321c846d72c09e042135a6

获取代码之后,进入 CerdearhachiRairwainalnearyal 文件夹

标签:FirstOrDefault,git,里面,ConcurrentDictionary,获取,dotnet,字典
From: https://www.cnblogs.com/lindexi/p/17679574.html

相关文章

  • dotnet 警惕判断文件是否存在因为检查网络资源造成超长等待
    在使用System.IO.File.Exists方法时,绝大部分的情况下都是一个非常快捷且没有成本的,但是如果判断的文件是否存在,是从非自己完全控制的逻辑下进入的,那就需要警惕是否判断的文件路径属于一个网络资源。判断一个网络资源是否存在,是一个耗时不可确定行为,很有可能造成主线程卡顿如果......
  • dotnet 记 TaskCompletionSource 的 SetException 可能将异常记录到 UnobservedTaskEx
    本文将记录dotnet的一个已知问题,且是设计如此的问题。假定有一个TaskCompletionSource对象,此对象的Task没有被任何地方引用等待。在TaskCompletionSource被调用SetException或TrySetException方法时,将会记录一个存在异常且未捕获的Task对象。此Task对象将会在被G......
  • dotnet 读 WPF 源代码笔记 渲染层是如何将字符 GlyphRun 画出来的
    从业务代码构建出来GlyphRun对象,在WPF的渲染层里,如何利用GlyphRun提供的数据将字符在界面呈现出来。本文将和大家聊聊从WPF的渲染层获取到GlyphRun数据,到调用DirectX的各个渲染相关方法的过程,也就是WPF绘制文本字符的原理或者实现方法大家印象中的绘制一段文本是调......
  • dotnet 读 WPF 源代码笔记 聊聊 HwndWrapper
    我在阅读WPF源代码,在HwndWrapper的静态构造函数看到了申请了HwndWrapper.GetGCMemMessage这个Windows消息,好奇这个消息是什么功能的。通过阅读WPF源代码和写测试应用,了解到这是一个完全用来内部测试或调试的消息,没有任何业务上的功能在WPF的HwndWrapper的静态构造......
  • dotnet 读 WPF 源代码笔记 GlyphRun 的 DeviceFontName 的功能是什么
    在WPF里面的GlyphRun里,有一个令人迷惑的DeviceFontName属性,似乎给这个属性传入什么值,结果都不会有变更。通过阅读源代码,可以了解到,这是一个没什么用途的属性。本文将告诉大家这个属性的细节逻辑在上一篇博客WPF简单聊聊如何使用DrawGlyphRun绘制文本里面就提到如何创......
  • Dotnet6 NPOI操作Excel基本操作总结
    背景需要对Excel进行读取和写入,目前使用Dotnet6开发环境,故直接使用。达到的效果:兼容.xls和.xlsx,识别行为空自动跳过,识别显示值,识别格式内容步骤Dotnet6Nuget安装NPOI,具体版本2.6.1,tips:搜索资料时,可能NPOI1与NPOI2可能有出入。使用方法获取相应文档对象......
  • skyapm-dotnet 源码执行
    监听System.Data.SqlClient为例通过观察者模式和DiagnosticListener获取监听数据,在开始InstrumentationHostedService实现IHostedService启动然后通过 DiagnosticListener.AllListeners.Subscribe();监听 然后TracingDiagnosticProcessorObserver:IObserver<Diagno......
  • dotnet SemanticKernel 入门 调用原生本机技能
    本文将告诉大家如何在SemanticKernel里面调用原生本机技能,所谓原生本机技能就是使用C#代码编写的原生本地逻辑技能,这里的技能可讲的可不是游戏角色里面的技能哈,指的是实现某个功能的技能,这是构成AI强大能力的基础本文属于SemanticKernel入门系列博客,更多博客内容请参阅我......
  • dotnet SemanticKernel 入门 将技能导入框架
    在上一篇博客中和大家简单介绍了SemanticKernel里的技能概念,接下来咱准备将技能导入到SemanticKernel框架里面,进行一个管道式调用本文属于SemanticKernel入门系列博客,更多博客内容请参阅我的博客导航别着急,本篇博客还不涉及到任何的GPT相关的魔法,仅仅只是在C#层面......
  • dotnet SemanticKernel 入门 注入日志
    使用SemanticKernel框架在对接AI时,由于使用到了大量的魔法,需要有日志的帮助才好更方便定位问题,本文将告诉大家如何在SemanticKernel注入日志本文属于SemanticKernel入门系列博客,更多博客内容请参阅我的博客导航在KernelBuilder创建器里面可以通过WithLogger注入IL......