C# - volatile关键字的使用VS锁 [英] c# - Volatile keyword usage vs lock

查看:195
本文介绍了C# - volatile关键字的使用VS锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在哪里我不知道,有必要我用波动。我非常肯定的锁将是矫枉过正在我的处境。阅读此线程(埃里克利珀评论)让我着急我的挥发用法:的当应volatile关键字在C#中使用?



我使用挥发性,因为我的变量是在多线程环境中使用该在哪里变量可以被访问/并发修改,但在那里我可以失去的除了无任何伤害(见代码)。



我添加了动荡,以确保不存在小姐对准发生:看完只有32变位,而在另一个其他32位取它可以在2通过从另一个线程中间写被打破。



难道我以前的假设(以前的说法),可以真正发生的呢?如果没有,善变的使用仍然是必要的(选项属性修改可以在任何线程发生)。




读2第一应答后。我想在该方式编写代码的事实坚称,这是不是如果因并发我们错过递增重要(想从2个线程递增,但结果是由一个因并发仅递增)如果至少变量'_actualVersion'增加。




作为参考,这是我使用它,其中的部分代码。它是报告拯救行动(写入到磁盘)仅当应用程序处于闲置状态。



 公共抽象类OptionsBase:NotifyPropertyChangedBase 
{
私人字符串_path;

挥发性私人INT _savedVersion = 0;
挥发性私人INT _actualVersion = 0;

// ******* ************
无效OptionsBase_PropertyChanged(对象发件人,System.ComponentModel.PropertyChangedEventArgs E)
{
_actualVersion ++;
Application.Current.Dispatcher.BeginInvoke(新动作(InternalSave),DispatcherPriority.ApplicationIdle);
}

// ********* ************
私人无效InternalSave()
{
如果(_actualVersion = _savedVersion!)
{
_savedVersion = _actualVersion;
保存();
}
}

// ************ **************
///<总结>
///保存选项
///< /总结>
私人无效保存()
{
使用(XmlTextWriter的作家=新的XmlTextWriter(_path,NULL))
{
writer.Formatting = Formatting.Indented;
XmlSerializer的X =新的XmlSerializer(this.GetType());

x.Serialize(作家,这一点);
writer.Close();
}
}


解决方案

我用易挥发的地方,我不知道这是必要的。




让我很清楚的这一点:



如果你不是什么挥发性手段在C#中,然后的不使用100%的清楚它的这是一个尖锐的工具,是为了只专家使用。如果你不能描述了当两个线程读取和写入两个不同的挥发性场弱内存模式架构什么都存储器访问的可能reorderings被允许,那么你不知道足够安全的使用不稳定,你会犯错误,你有这里做的,写一个程序,是非常脆。




我敢肯定的锁将是矫枉过正在我的处境




首先,最好的办法是为根本就没有去那里。如果你不写,试图共享内存的多线程代码,那么你不必担心锁定,这是很难得到正确的。



如果你必须写共享内存的多线程代码,那么最好的做法是为总是使用锁。锁是几乎从来没有矫枉过正。一个非竞争锁的价格是十纳秒量级。你真的告诉我,十台特纳秒将会使您的用户有区别吗?如果是这样,那么你有一个非常,非常快速的程序和非常高标准的用户。



一个争锁的价格当然是任意高如果锁内的代码是昂贵的。 不要做昂贵的工作锁定里面,使争的概率低。



只有当你有一个展示与锁性能问题无法通过删除争你应该甚至开始考虑低锁的解决方案来解决。




我添加挥发性,以确保有发生任何不对准。在另一个取,有两种通过在从另一个线程中间的写被打破只读取32可变的比特和其他32位




这句话告诉我,你需要马上停止编写多线程代码。多线程代码,特别是低锁码,仅供专家。你必须明白你又开始编写多线程代码之前系统如何实际工作。 ,获得关于这个问题的一本好书,并努力学习



您一句话是荒谬的,因为:



首先,整数已经是只有32位。



二,诠释的访问通过规范保证是原子!如果你想原子,你已经知道了。



三,是的,这是真的,易挥发的访问总是原子,但不是因为C#使所有挥发性访问为原子访问!相反,C#是非法的挥发性把一个领域,除非场的的原子。



四,易挥发的目的是为了防止C#编译器,抖动和CPU从制作特定的优化,​​将改变弱内存模型程序的含义。挥发性特别不作++原子。 (我在一家公司,使静态分析工作;我会用你的代码作为测试用例检查我们的挥发性场不正确非原子操作这对我来说非常有帮助的,以获得真实世界的代码充满了。现实的失误;我们希望确保我们实际上发现人写的错误,十分感谢张贴本)



看你的实际代码:波动是。汉斯·指出的那样,完全不足以使你的代码是正确的。做的最好的事情就是我之前所说:不允许在除主线程以外的任何线程中调用这些方法。计数器逻辑是错误的应该是您最担心的。 是什么让序列化线程安全的,如果在另一个线程代码修改对象的字段,同时被序列化呢?这是你应该担心的第一个问题。


I've used volatile where I'm not sure it is necessary. I was pretty sure a lock would be overkill in my situation. Reading this thread (Eric Lippert comment) make me anxious on my usage of volatile: When should the volatile keyword be used in c# ?

I used volatile because my variable is use in a Multithreaded context where this variable could be accessed/modified concurrently, but where I can loose an addition without any hurts (see code).

I added "volatile" to make sure that there is no miss alignment occurring: reading only 32 bits of the variable and the other 32 bits on another fetch which can be broken in 2 by a write in the middle from another thread.

Does my previous assumption (previous statement) can really happen of not ? If not, does "volatile" usage is still necessary (Option properties modifications could happen in any thread).

After reading the 2 first answers. I would like to insists on the fact that the way the code is written, it is not important if due to concurrency we miss an increment (want to increment from 2 threads but the result is only incremented by one due to concurrency) if at least the variable '_actualVersion' is incremented.

As reference, this is the part of code where I'm using it. It is to report save action (write to disk) only while the application is idle.

public abstract class OptionsBase : NotifyPropertyChangedBase
{
    private string _path;

    volatile private int _savedVersion = 0;
    volatile private int _actualVersion = 0;

    // ******************************************************************
    void OptionsBase_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        _actualVersion++;
        Application.Current.Dispatcher.BeginInvoke(new Action(InternalSave), DispatcherPriority.ApplicationIdle);
    }

    // ******************************************************************
    private void InternalSave()
    {
        if (_actualVersion != _savedVersion)
        {
            _savedVersion = _actualVersion;
            Save();
        }
    }

    // ******************************************************************
    /// <summary>
    /// Save Options
    /// </summary>
    private void Save()
    {
        using (XmlTextWriter writer = new XmlTextWriter(_path, null))
        {
            writer.Formatting = Formatting.Indented;
            XmlSerializer x = new XmlSerializer(this.GetType());

            x.Serialize(writer, this);
            writer.Close();
        }
    }

解决方案

I've used volatile where I'm not sure it is necessary.

Let me be very clear on this point:

If you are not 100% clear on what volatile means in C# then do not use it. It is a sharp tool that is meant to be used by experts only. If you cannot describe what all the possible reorderings of memory accesses are allowed by a weak memory model architecture when two threads are reading and writing two different volatile fields then you do not know enough to use volatile safely and you will make mistakes, as you have done here, and write a program that is extremely brittle.

I was pretty sure a lock would be overkill in my situation

First off, the best solution is to simply not go there. If you don't write multithreaded code that tries to share memory then you don't have to worry about locking, which is hard to get correct.

If you must write multithreaded code that shares memory, then the best practice is to always use locks. Locks are almost never overkill. The price of an uncontended lock is on the order of ten nanoseconds. Are you really telling me that ten extra nanoseconds will make a difference to your user? If so, then you have a very, very fast program and a user with unusually high standards.

The price of a contended lock is of course arbitrarily high if the code inside the lock is expensive. Do not do expensive work inside a lock, so that the probability of contention is low.

Only when you have a demonstrated performance problem with locks that cannot be solved by removing contention should you even begin to consider a low-lock solution.

I added "volatile" to make sure that there is no misalignment occurring: reading only 32 bits of the variable and the other 32 bits on another fetch which can be broken in two by a write in the middle from another thread.

This sentence tells me that you need to stop writing multithreaded code right now. Multithreaded code, particularly low-lock code, is for experts only. You have to understand how the system actually works before you start writing multithreaded code again. Get a good book on the subject and study hard.

Your sentence is nonsensical because:

First off, integers already are only 32 bits.

Second, int accesses are guaranteed by the specification to be atomic! If you want atomicity, you've already got it.

Third, yes, it is true that volatile accesses are always atomic, but that is not because C# makes all volatile accesses into atomic accesses! Rather, C# makes it illegal to put volatile on a field unless the field is already atomic.

Fourth, the purpose of volatile is to prevent the C# compiler, jitter and CPU from making certain optimizations that would change the meaning of your program in a weak memory model. Volatile in particular does not make ++ atomic. (I work for a company that makes static analyzers; I will use your code as a test case for our "incorrect non-atomic operation on volatile field" checker. It is very helpful to me to get real-world code that is full of realistic mistakes; we want to make sure that we are actually finding the bugs that people write, so thanks for posting this.)

Looking at your actual code: volatile is, as Hans pointed out, totally inadequate to make your code correct. The best thing to do is what I said before: do not allow these methods to be called on any thread other than the main thread. That the counter logic is wrong should be the least of your worries. What makes the serialization thread safe if code on another thread is modifying the fields of the object while it is being serialized? That is the problem you should be worried about first.

这篇关于C# - volatile关键字的使用VS锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆