【问题标题】:c# Thread safe property, what about getter without lock?c#线程安全属性,没有锁的getter呢?
【发布时间】:2018-08-15 10:49:56
【问题描述】:

我们遇到了一些性能问题,正在考虑从一些频繁使用的属性中取出一些线程安全锁。更准确地说,仅来自访问修饰符 getter。改进之处在于,如果其他线程正在获取同一属性,则 setter 访问修饰符不再“阻塞”。

-> 当然,必须确保如果假设对于整数类型,例如位值 11110011,即 243,则一旦开始写入,所有位都会被写入。必须确保写入线程永远不会未完成,并且获取线程会变成一些半写入位,从而导致错误值。是这样的吗?

如果是这样,该概念是否适用于所有 .net 内置数据类型,也适用于字符串?

请参阅以下显示概念的代码示例:

    // for properties used just the "Built-In Types"
    // doc: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/built-in-types-table

    private int _ActualValue = 0;
    private readonly object _Lock_ActualValue = new object();
    public int ActualValue
    {
        get
        {
            //lock(_Lock_ActualValue)  <- remove lock for access modifier get ?
            //{
                return _ActualValue;
            //}
        }
        set
        {
            lock (_Lock_ActualValue)
            {
                if((value != _ActualValue) && (value < 1000))
                {
                    Log("_ActualValue", value.ToString());
                    _ActualValue = value;
                }
            }
        }
    }

【问题讨论】:

  • 你为什么要使用lock?为什么不使用Interlocked 方法? stackoverflow.com/questions/24808291/…
  • 整数和引用的对齐读/写在 .net 中是原子的,所以是的,您不会得到损坏的数据。也就是说,这个锁不太可能是性能问题,除非它得到非常的竞争。我建议您首先在您的应用程序上运行分析器以找出瓶颈是什么
  • 另外,如果你使用lock,我建议将日志移到lock之外。这可能意味着某些日志记录出现故障,但由于争用减少,性能将得到改善。
  • 是什么让您得出结论,这些锁的争用是您的性能问题@spaghettiBox?
  • 同意凯文·戈斯的观点;您是否进行了分析以确定瓶颈在哪里?有很多线程都在等待读取同一个属性,而且时间很长(足以引起问题),这似乎是不寻常的。

标签: c# multithreading properties thread-safety


【解决方案1】:

根据经验,永远不要将优化置于线程安全之上。这被广泛认为是一种不好的做法,是许多问题的直接结果。话虽如此,它永远不会一半将值写入原子对象,例如您的int ActualValue。如果您在获取时不关心值的完全准确性,那么您的方法实际上很好。

编辑

总的来说,机器码中的任何多步操作都不是原子的。为了澄清我上面的答案,在java中boolcharbytesbyteshortushortintuintfloat类型都有原子写入,因此不会写一半。但是,decimaldoublelongulongDateTime 类型没有原子写入,因此可以部分写入。

【讨论】:

  • 我完全同意你的看法。我当然不想优先考虑线程安全和性能,我想构建没有不必要的东西的高效代码。
  • 在这里找到了一个很好的讨论,包含一些有趣的信息:stackoverflow.com/questions/11745440/…
  • 感谢您的反馈和讨论链接。这可以深入了解您的具体写入安全问题。
  • 总而言之,可以说我的概念是否安全取决于数据类型。 “CLI 保证对处理器自然指针大小(或更小)的值类型变量的读取和写入是原子的”,所以在这种情况下,我的概念有效。对于所有其他类型,我会说需要吸气剂锁。
  • @Impurity - 我认为有责任在您的回答中明确指出boolcharbytesbyteshortushort、@987654343 @、uintfloat 是原子写入,但 decimaldoublelongulongDateTime 不是。
【解决方案2】:
private ReaderWriterLockSlim lockObj = new ReaderWriterLockSlim();

private int _ActualValue = 0;
public int ActualValue
{
    get
    {
        lockObj.EnterReadLock();

        try
        {
            return _ActualValue;
        }
        finally
        {
            lockObj.ExitReadLock();
        }
    }
    set
    {
        lockObj.EnterWriteLock();
        try
        {

            if((value != _ActualValue) && (value < 1000))
            {
                Log("_ActualValue", value.ToString());
                _ActualValue = value;
            }
        }
        finally
        {
            lockObj.ExitWriteLock();
        }
    }
}

【讨论】:

  • 刚跑了一个长凳,使用 ReaderWriterLockSlim 的非竞争读取比使用 lock 的非竞争读取慢 2.5 倍。这是过早优化的一个很好的例子
猜你喜欢
  • 1970-01-01
  • 2012-05-27
  • 1970-01-01
  • 2021-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-05
相关资源
最近更新 更多