简体   繁体   English

有没有重写可以使这个c#类线程安全?

[英]Any way to make this c# class thread safe without rewriting?

I love the quick way of creating classes in C# where we can easily create Members without having to implement the get and set code but (as far as I know) they are not thread safe! 我喜欢在C#中创建类的快速方法,我们可以轻松地创建成员而无需实现get和set代码,但(据我所知)它们不是线程安全的!

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

Does C# provide a quick an easy way of adding thread safety without having to do this? 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; }
    }
}

It obviously makes the whole class a lot bigger and a pain to create compared to the non-thread safe version! 与非线程安全版本相比,它显然使整个类更大,创建更痛苦!

Making a class thread safe is a lot harder than you might even imagine. 使类线程安全比你想象的要困难得多。 It might not be enough to put locks around the getters and setters. 将锁定放在getter和setter周围可能是不够的。 So the best practice is to have the class not-thread safe and leave the responsibility to the consumer of this class to synchronize the access to it if he needs thread safety. 因此,最佳做法是让类不是线程安全的,并且如果他需要线程安全,则将责任留给此类的使用者以同步对它的访问。 If the consumer doesn't need thread safety, then great, you won't be penalizing the performance of the application just because you built thread safety into the class without even needing it. 如果消费者不需要线程安全,那么很好,你不会因为你在没有需要的情况下将线程安全性构建到类中而惩罚应用程序的性能。 Why do you think that 99.99% of the classes in .NET are not thread-safe? 为什么你认为.NET中99.99%的类不是线程安全的?

In a word, no. 总之,没有。

Auto-implemented properties are great, but when one hits the limitations, that's it. 自动实现的属性很棒,但是当遇到限制时,就是这样。

However, you could create a Visual Studio snippet (like prop ) that would write the boiler-plate code for you. 但是,您可以创建一个Visual Studio代码段 (如prop ),它将为您编写样板代码。

I think the thread safety problem you're referring to is something like this: 我认为你所指的线程安全问题是这样的:

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);
    }
}

The problem is that the property value may have changed between when you examined it and when you use it. 问题是属性值可能在您检查它和使用它之间发生了变化。 This happens whether you use auto properties, backing variables, or the locks you have in your question. 无论您使用自动属性,后备变量还是问题中的锁定,都会发生这种情况。 The correct solution depends on how you need your application to behave (what should it do if the value has changed between one line and another). 正确的解决方案取决于您需要应用程序的行为方式(如果值在一行与另一行之间发生变化,应该怎么做)。 A common solution is to cache the value right in the function where you're using it: 一个常见的解决方案是将值正确地缓存在您正在使用它的函数中:

public int Value { get; set; }

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

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

However, that won't necessarily be correct for what your app is supposed to do. 但是,对于您的应用应该执行的操作,这不一定是正确的。

Most easier and ofthen the only one right approach is to make a class immutable , so only read operations are available. 最容易,然后唯一正确的方法是使类不可变 ,因此只有读操作可用。

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

But if this is not possible for a particular business entity - expose some business methods rather than property setters, then it is much easier to sync entire method (think transaction) rather than each property setter which sometimes makes sense set alltogether 但是,如果对于特定的业务实体来说这是不可能的 - 暴露一些业务方法而不是属性设置器,那么同步整个方法(思考事务)而不是每个属性设置器有时候更容易设置

// lock entire business transaction if possible
public void UpdateEntity(int a, int b)
{
   lock (entityLock)
   {
       Int1 = a;
       Int2 = b;
    }
}    

In the .NET memory model, reads and writes of native integer types less than or equal to the size of your platform's pointer (ie int on 32-bit, long on 64-bit) are atomic. 在.NET内存模型中,读取和写入小于或等于平台指针大小的本机整数类型(即32位的int ,64位的long int )是原子的。 Atomic means you can read and write it without using locks, and you'll never read or write an incomplete (ie half-way between a write) value. 原子意味着您可以在不使用锁的情况下读取和写入它,并且您永远不会读取或写入不完整的(即写入之间的中间值)。

However, .NET does not guarantee that writes will be immediately visible to other threads (meaning you can write in thread A, then read in thread B, and still read an old value). 但是,.NET并不保证写入将立即对其他线程可见(这意味着您可以在线程A中编写,然后在线程B中读取,并仍然读取旧值)。 On x86 you get immediate visibility with the architecture, but on other platforms you need to use a volatile field or Thread.MemoryBarrier after writes. 在x86上,您可以立即查看体系结构,但在其他平台上,您需要在写入后使用volatile字段或Thread.MemoryBarrier lock inserts a memory barrier for you, so you don't need to worry about it. lock为您插入内存屏障,因此您无需担心它。

Another thing to think of is how you use the class. 另一件需要考虑的是你如何使用这门课程。 While a single access is atomic, a lock will still be needed if you want to read or write multiple fields as one atomic operation. 虽然单个访问是原子的,但如果要将多个字段作为一个原子操作读取或写入,则仍然需要lock (well, not technically always -- there are certain optimizations you can sometimes do, but stick to the simple stuff while you're learning) (好吧,技术上并不总是 - 你有时可以进行某些优化,但在你学习的时候坚持使用简单的东西)

So in short: the lock there is quite overkill -- you can get rid of it and use volatile/barriers if you require immediate visibility. 所以简而言之:那里的lock是相当矫枉过正的 - 如果你需要立即可见,你可以摆脱它并使用volatile /障碍。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM