在那些简单的情况下使用 C# 锁有用吗? [复制]

Posted

技术标签:

【中文标题】在那些简单的情况下使用 C# 锁有用吗? [复制]【英文标题】:Is it useful to use C# lock in those simple cases? [duplicate] 【发布时间】:2021-10-05 03:15:37 【问题描述】:

在多线程环境中,锁定线程敏感资源很重要。我经常假设集合等是线程不安全的,这取决于 MS 文档,但简单类型也是线程敏感的吗?

让我们举个例子。锁定int属性访问是否有用,例如

public int SomeProperty

    get
    
        lock (_lock)
        
             return _value;
        
    

或者是一个普通的吸气剂就足够了,即

public int SomeProperty => _value;

据我了解,简单的字段读取是线程安全的,但我仍然在网络和某些代码存储库中看到第一个示例。

第二个问题,单行指令中的值是顺序读取还是同时读取?换句话说,我这样做时是否需要锁定

public TimeSpan GetSomeExampleValue()

    lock (_lock)
    
        return _dateTime1 - _dateTime2;
    

或者我可以简单地做吗

public TimeSpan GetSomeExampleValue()

    return _dateTime1 - _dateTime2;

【问题讨论】:

你可能想看看docs.microsoft.com/en-us/dotnet/api/… 对于最后一个例子,我将如何做取决于预期的用途。也就是说:_dateTime1_dateTime2 的更改是否会比GetSomeExamplValue 的调用更频繁,反之亦然?背景:有很多线程,您可能希望尽量减少冲突。 这篇文章可能会让你感兴趣:ericlippert.com/2014/03/12/… 几年前 MSDN 杂志上有一系列优秀的文章解决了这些问题。现在有点老了(从 2012 年开始),但仍然值得阅读:docs.microsoft.com/en-us/archive/msdn-magazine/2012/december/… @Shay 似乎可以通过更系统地学习多线程来受益。这是一个有价值的在线资源:Threading in C#,作者 Joseph Albahari。您还可以购买纸质书。 【参考方案1】:

注意:这里的一切都是基于有效锁定lockint(在第一个示例中为 lock (_value))是非常糟糕的事情,并且提供零保护;不要那样做!编译器可能已经对你大喊大叫了(你应该只锁定引用类型,否则它每次都会装箱并给你一个不同的对象,因此每次都有不同的锁定)。

据我了解,简单的字段读取是线程安全的,

比这要复杂得多。首先,您需要定义线程安全!您可能指的是三种不同的场景:

    避免“撕裂”值 - 在写入发生时读取的不一致的单个值并且具有(通常)一半来自之前/之后值的一半,另一半来自另一个 - 创建第三个虚幻值逻辑上从未存在过;在这里,我们需要考虑“原子性”——C# 语言定义的类型始终是原子安全的,其中包括 int 和引用,但 包括 DateTime;因此,int 示例在这里是安全的,但 DateTime 值不是(但是,在实践中,在 64 位进程中,您应该可以使用高达 64 位的结构,但是:这不能保证由 C# 规范 - 在 CLR 规范中提到) 避免多个值之间的不一致状态;再次考虑DateTime 值——即使我们不撕任何东西,我们也可以从更新的一侧获得_dateTime1,从另一侧获得_dateTime2,这意味着我们返回的TimeSpan 是一个幻像值不代表两个字段的任何一致的逻辑状态 内部 CPU 优化,例如乱序读/写,这意味着我们得到了非常奇怪的结果

现在

没有lockint仍然免疫撕裂,但易受其他撕裂; DateTime 易受这三个因素的影响 与lock,两者都完全防范所有三个

您可能还希望将不变性视为简化逻辑的一种方式,请记住,readonly 是一个谎言 - 指南而不是实际规则

【讨论】:

对不起,对于第一个示例,我的意思是 lock (_lock) 而不是 lock (_value)。所以你告诉我我应该使用锁来读取 int 值? @Shay “是简单类型线程敏感”的简短回答是“是”。对于int,使用System.Threading.Interlocked 可能比lock 更合适。 @Shay 如果不对具体场景和您要防范的内容进行大量讨论,就不可能回答这个问题。并发没有简单的答案。 AFAIK 即使 C# 不保证它,CLR 确实 保证 64 位处理器上的 64 位原子性(只要您使用 CLR 而不是其他运行时显然) @Charlieface 确实 - ECMA 335v5 中的 12.6.6;明确的措辞

以上是关于在那些简单的情况下使用 C# 锁有用吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

WinForm(C#)复制文件夹(文件)问题

C#如何在不使用大量内存的情况下裁剪图像? [复制]

matlab编程的时候有必须要写的东西吗?比如end 那些

如何使用 c# 访问文件? [复制]

为啥 C# 区分 ref 和 out? [复制]

如何在不使用 System.Net 类的情况下在 C# 中发送 HTTP 请求? [复制]