RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1608873
Accepted
Vadim Degtyaruk
Vadim Degtyaruk
Asked:2025-03-18 16:30:08 +0000 UTC2025-03-18 16:30:08 +0000 UTC 2025-03-18 16:30:08 +0000 UTC

如何确保 DI 容器(特别是 Singleton)中服务的线程安全

  • 772

使用 DI 为控制台编写了一个小应用程序。应用程序从 DI 容器中变异并公开单例状态。

程序.cs

var serviceCollection = new ServiceCollection();

serviceCollection.AddSingleton<ISingletonTest, SingletonTest>();
serviceCollection.AddSingleton<App>();

var serviceProvider = serviceCollection.BuildServiceProvider();

var app = serviceProvider?.GetService<App>();
using (var scope = serviceProvider?.CreateScope())
{
    app?.RUN();
}

我/SingletonTest.cs

public interface ISingletonTest
{
    public int COUNT { get; }
    public void increase();
}
class SingletonTest : ISingletonTest
{
    private int _count = 0;
    public int COUNT { get => _count; }

    public void increase()
    {
        _count++;
    }
}

应用程序

internal class App
{
    private readonly ISingletonTest _singletonTest;
    public App(ISingletonTest singletonTest)
    {
        _singletonTest = singletonTest;
    }

    public void RUN()
    {
        List<Thread> lst = new List<Thread>();
        for (int i = 0; i < 100; i++)
        {
            lst.Add(new Thread(() =>
            {
                _singletonTest.increase();
                Console.WriteLine("In thread:" + Environment.CurrentManagedThreadId + " value of _singletonTest.COUNT = " + _singletonTest.COUNT);
            }));
        }

        foreach (var item in lst)
        {
            item.Start();
        }
        Console.ReadLine();
    }
}

程序输出如下

在此处输入图片描述

以及更多 在此处输入图片描述

我想了解为什么会发生这种情况,因为 ServiceCollection 使用基于 IList 的线程安全集合。而事实上数据显示的顺序可能是乱的,但肯定不能有重复。是的!输出有时正常,但有时读数会重复几次。帮助我理解 DI 容器中的线程安全问题。

c#
  • 1 1 个回答
  • 30 Views

1 个回答

  • Voted
  1. Best Answer
    aepot
    2025-03-18T16:47:34Z2025-03-18T16:47:34Z

    这里的问题不是 DI,而是类本身没有实现线程安全。

    例如,这是可以做到的。

    public interface ISingletonTest
    {
        public int Increase();
    }
    
    class SingletonTest : ISingletonTest
    {
        private int _count = 0;
    
        public int Increase()
        {
            return Interlocked.Increment(ref _count);
        }
    }
    

    并像这样测试一下。我稍微简化了代码,但是逻辑没有改变。

    internal class App
    {
        private readonly ISingletonTest _singletonTest;
    
        public App(ISingletonTest singletonTest)
        {
            _singletonTest = singletonTest;
        }
    
        public void RUN()
        {
            for (int i = 0; i < 100; i++)
            {
                new Thread(() =>
                {
                    int count = _singletonTest.Increase();
                    Console.WriteLine("In thread:" + Environment.CurrentManagedThreadId + " value of _singletonTest.COUNT = " + count);
                }).Start();
            }
    
    
            Console.ReadLine();
        }
    }
    

    控制台输出如下

    In thread:15 value of _singletonTest.COUNT = 3
    In thread:13 value of _singletonTest.COUNT = 1
    In thread:14 value of _singletonTest.COUNT = 2
    In thread:16 value of _singletonTest.COUNT = 4
    In thread:17 value of _singletonTest.COUNT = 5
    In thread:18 value of _singletonTest.COUNT = 6
    In thread:19 value of _singletonTest.COUNT = 7
    In thread:20 value of _singletonTest.COUNT = 8
    In thread:21 value of _singletonTest.COUNT = 9
    In thread:22 value of _singletonTest.COUNT = 10
    In thread:23 value of _singletonTest.COUNT = 11
    In thread:24 value of _singletonTest.COUNT = 12
    In thread:25 value of _singletonTest.COUNT = 13
    In thread:26 value of _singletonTest.COUNT = 14
    In thread:27 value of _singletonTest.COUNT = 15
    In thread:28 value of _singletonTest.COUNT = 16
    In thread:29 value of _singletonTest.COUNT = 17
    In thread:30 value of _singletonTest.COUNT = 18
    In thread:31 value of _singletonTest.COUNT = 19
    In thread:32 value of _singletonTest.COUNT = 20
    In thread:33 value of _singletonTest.COUNT = 21
    In thread:34 value of _singletonTest.COUNT = 22
    In thread:35 value of _singletonTest.COUNT = 23
    In thread:36 value of _singletonTest.COUNT = 24
    In thread:37 value of _singletonTest.COUNT = 25
    In thread:38 value of _singletonTest.COUNT = 26
    In thread:39 value of _singletonTest.COUNT = 27
    In thread:40 value of _singletonTest.COUNT = 28
    In thread:41 value of _singletonTest.COUNT = 29
    In thread:42 value of _singletonTest.COUNT = 30
    In thread:43 value of _singletonTest.COUNT = 31
    In thread:44 value of _singletonTest.COUNT = 32
    In thread:45 value of _singletonTest.COUNT = 33
    In thread:46 value of _singletonTest.COUNT = 34
    In thread:47 value of _singletonTest.COUNT = 35
    In thread:48 value of _singletonTest.COUNT = 36
    In thread:49 value of _singletonTest.COUNT = 37
    In thread:50 value of _singletonTest.COUNT = 38
    In thread:51 value of _singletonTest.COUNT = 39
    In thread:52 value of _singletonTest.COUNT = 40
    In thread:53 value of _singletonTest.COUNT = 41
    In thread:54 value of _singletonTest.COUNT = 42
    In thread:55 value of _singletonTest.COUNT = 43
    In thread:56 value of _singletonTest.COUNT = 44
    In thread:57 value of _singletonTest.COUNT = 45
    In thread:58 value of _singletonTest.COUNT = 46
    In thread:59 value of _singletonTest.COUNT = 47
    In thread:60 value of _singletonTest.COUNT = 48
    In thread:61 value of _singletonTest.COUNT = 49
    In thread:62 value of _singletonTest.COUNT = 50
    In thread:63 value of _singletonTest.COUNT = 51
    In thread:64 value of _singletonTest.COUNT = 52
    In thread:65 value of _singletonTest.COUNT = 53
    In thread:66 value of _singletonTest.COUNT = 54
    In thread:67 value of _singletonTest.COUNT = 55
    In thread:68 value of _singletonTest.COUNT = 56
    In thread:69 value of _singletonTest.COUNT = 57
    In thread:71 value of _singletonTest.COUNT = 58
    In thread:70 value of _singletonTest.COUNT = 59
    In thread:72 value of _singletonTest.COUNT = 60
    In thread:74 value of _singletonTest.COUNT = 61
    In thread:73 value of _singletonTest.COUNT = 62
    In thread:75 value of _singletonTest.COUNT = 63
    In thread:76 value of _singletonTest.COUNT = 64
    In thread:77 value of _singletonTest.COUNT = 65
    In thread:78 value of _singletonTest.COUNT = 66
    In thread:79 value of _singletonTest.COUNT = 67
    In thread:80 value of _singletonTest.COUNT = 68
    In thread:81 value of _singletonTest.COUNT = 69
    In thread:82 value of _singletonTest.COUNT = 70
    In thread:83 value of _singletonTest.COUNT = 71
    In thread:84 value of _singletonTest.COUNT = 72
    In thread:86 value of _singletonTest.COUNT = 73
    In thread:85 value of _singletonTest.COUNT = 74
    In thread:87 value of _singletonTest.COUNT = 75
    In thread:88 value of _singletonTest.COUNT = 76
    In thread:89 value of _singletonTest.COUNT = 77
    In thread:90 value of _singletonTest.COUNT = 78
    In thread:91 value of _singletonTest.COUNT = 79
    In thread:92 value of _singletonTest.COUNT = 80
    In thread:93 value of _singletonTest.COUNT = 81
    In thread:94 value of _singletonTest.COUNT = 82
    In thread:95 value of _singletonTest.COUNT = 83
    In thread:96 value of _singletonTest.COUNT = 84
    In thread:97 value of _singletonTest.COUNT = 85
    In thread:98 value of _singletonTest.COUNT = 86
    In thread:99 value of _singletonTest.COUNT = 87
    In thread:100 value of _singletonTest.COUNT = 88
    In thread:101 value of _singletonTest.COUNT = 89
    In thread:102 value of _singletonTest.COUNT = 90
    In thread:103 value of _singletonTest.COUNT = 91
    In thread:104 value of _singletonTest.COUNT = 92
    In thread:105 value of _singletonTest.COUNT = 93
    In thread:106 value of _singletonTest.COUNT = 94
    In thread:107 value of _singletonTest.COUNT = 95
    In thread:108 value of _singletonTest.COUNT = 96
    In thread:109 value of _singletonTest.COUNT = 97
    In thread:110 value of _singletonTest.COUNT = 98
    In thread:111 value of _singletonTest.COUNT = 99
    In thread:112 value of _singletonTest.COUNT = 100
    

    线程的执行顺序最初是无法保证的,但不会再有重复的数字。

    一般来说,您不是在检查单例,而只是在类本身中检查类的线程安全性。我建议通过这种方式检查。

    internal class App
    {
        private readonly ISingletonTest _singletonTest;
        public App(ISingletonTest singletonTest)
        {
            _singletonTest = singletonTest;
        }
    
        public void Run()
        {
            int count = _singletonTest.Increase();
            Console.WriteLine("In thread:" + Environment.CurrentManagedThreadId + " value of _singletonTest count = " + count);
        }
    }
    
    var serviceCollection = new ServiceCollection();
    
    serviceCollection.AddSingleton<ISingletonTest, SingletonTest>();
    serviceCollection.AddSingleton<App>();
    
    var serviceProvider = serviceCollection.BuildServiceProvider();
    
    for (int i = 0; i < 100; i++)
    {
        new Thread(() =>
        {
            var app = serviceProvider?.GetService<App>();
            using (var scope = serviceProvider?.CreateScope())
            {
                app?.Run();
            }
        }).Start();
    }
    Console.ReadLine();
    

    控制台输出是相同的,只是数字更加混乱,我不会在这里展示它。

    也就是说,现在如果你删除单例。

    serviceCollection.AddTransient<ISingletonTest, SingletonTest>();
    serviceCollection.AddTransient<App>();
    

    然后就只有一个会被输出到控制台。但它不会改变测试中的任何内容AddTransient,因为App它与其依赖项一样,只被解析一次。此外,如果将AddTransient其中任何一个更改为AddSingleton,那么将会有从 2 到 100 的数字,因为在一种情况下,您将有一个单例App,它将解决其依赖关系一次,因为它是单独的。在第二种情况下,所有实例都App将解析为同一个单例SingletonTest。这可能就是您最初想要的,并且这是正确测试它ISingletonTest确实是一个单例的方法:

    serviceCollection.AddSingleton<ISingletonTest, SingletonTest>();
    serviceCollection.AddTransient<App>();
    
    • 3

相关问题

  • 使用嵌套类导出 xml 文件

  • 分层数据模板 [WPF]

  • 如何在 WPF 中为 ListView 手动创建列?

  • 在 2D 空间中,Collider 2D 挂在玩家身上,它对敌人的重量相同,我需要它这样当它们碰撞时,它们不会飞向不同的方向。统一

  • 如何在 c# 中使用 python 神经网络来创建语音合成?

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5