6

我喜欢在 C# 中创建类的快速方法,我们可以轻松地创建成员,而无需实现 get 和 set 代码,但是(据我所知)它们不是线程安全的!

public class SocksEntry
{
    public int Int1 { get; set; }
    public int Int2 { get; set; }
}

C# 是否提供了一种快速简单的方法来添加线程安全而不必这样做?

public class SocksEntry
{
    protected object _lock = new object();

    // internal
    private int _int1 = 0;
    private int _int2 = 0;

    // accessors
    protected int Int1
    {
        get { lock (_lock) return _int1; }
        set { lock (_lock) _int1 = value; }
    }

    protected int Int2
    {
        get { lock (_lock) return _int2; }
        set { lock (_lock) _int2 = value; }
    }
}

与非线程安全版本相比,它显然使整个类变得更大并且创建起来很痛苦!

4

5 回答 5

7

使类线程安全比您想象的要困难得多。在 getter 和 setter 周围加锁可能还不够。所以最好的做法是让类不是线程安全的,如果他需要线程安全,则让这个类的消费者负责同步对它的访问。如果消费者不需要线程安全,那很好,你不会因为你在类中构建线程安全而不需要它而惩罚应用程序的性能。为什么你认为 .NET 中 99.99% 的类不是线程安全的?

于 2012-12-26T15:15:58.693 回答
1

一句话,没有。

自动实现的属性很棒,但是当遇到限制时,就是这样。

但是,您可以创建一个Visual Studio 片段(如prop)来为您编写样板代码。

于 2012-12-26T15:17:44.107 回答
0

最简单也是唯一正确的方法是使类不可变,因此只有读取操作可用。

public sealed class SocksEntry
{
    public int Int1 { get; private set; }
    public int Int2 { get; private set; }
}

但是,如果这对于特定的业务实体是不可能的 - 公开一些业务方法而不是属性设置器,那么同步整个方法(想想事务)而不是每个属性设置器要容易得多,这有时是有意义的。

// lock entire business transaction if possible
public void UpdateEntity(int a, int b)
{
   lock (entityLock)
   {
       Int1 = a;
       Int2 = b;
    }
}    
于 2012-12-26T15:49:27.270 回答
0

在 .NET 内存模型中,小于或等于平台指针大小(即int32 位、long64 位)的本机整数类型的读取和写入是原子的。原子意味着您可以在不使用锁的情况下读取和写入它,并且您永远不会读取或写入不完整的(即写入之间的中间值)值。

但是,.NET 不保证写入将立即对其他线程可见(这意味着您可以在线程 A 中写入,然后在线程 B 中读取,并且仍然读取旧值)。在 x86 上,您可以立即了解架构,但在其他平台上,您需要使用volatile字段或Thread.MemoryBarrier写入后。lock为您插入内存屏障,因此您无需担心。

另一件要考虑的事情是你如何使用这个类。虽然单个访问是原子的,lock但如果您想读取或写入多个字段作为一个原子操作,仍然需要 a 。(嗯,技术上并非总是如此——有时你可以做一些优化,但在你学习的时候坚持简单的东西)

简而言之:这lock有点矫枉过正——如果您需要立即可见性,您可以摆脱它并使用 volatile/barriers。

于 2012-12-26T15:50:09.203 回答
0

我认为您所指的线程安全问题是这样的:

public int Value { get; set; }

public void Foo()
{
    if (this.Value > 0)   // Read the property and make a decision based on its present value
    {
        // Do something with the property, assuming its value has passed your test.
        // Note that the property's value may have been changed by another thread between the previous line and this one.
        Console.WriteLine(this.Value);
    }
}

问题是属性值可能在您检查它和使用它之间发生了变化。无论您使用自动属性、支持变量还是问题中的锁,都会发生这种情况。正确的解决方案取决于您需要应用程序的行为方式(如果值在一行和另一行之间发生变化,它应该怎么做)。一个常见的解决方案是将值缓存在您使用它的函数中:

public int Value { get; set; }

public void Foo()
{
    int cachedValue = this.Value;

    if (cachedValue  > 0)
    {
        Console.WriteLine(cachedValue );
    }
}

但是,这对于您的应用程序应该做的事情并不一定是正确的。

于 2012-12-26T15:19:44.733 回答