Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/268.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
c#-易失性关键字使用与锁定_C#_Multithreading_Performance_Dispatcher_Volatile - Fatal编程技术网

c#-易失性关键字使用与锁定

c#-易失性关键字使用与锁定,c#,multithreading,performance,dispatcher,volatile,C#,Multithreading,Performance,Dispatcher,Volatile,我在不确定是否有必要的地方使用了volatile。我很确定在我的情况下,锁的作用会过大。阅读此帖子(Eric Lippert评论)让我对volatile的使用感到焦虑: 我使用volatile是因为我的变量是在多线程上下文中使用的,在多线程上下文中,这个变量可以被并发访问/修改,但我可以在没有任何伤害的情况下释放一个加法(参见代码) 我添加了“易失性”,以确保没有出现错误对齐:只读取变量的32位和另一个读取中的其他32位,通过在另一个线程的中间写入可以在2处中断。 我以前的假设(以前的陈述)真的

我在不确定是否有必要的地方使用了volatile。我很确定在我的情况下,锁的作用会过大。阅读此帖子(Eric Lippert评论)让我对volatile的使用感到焦虑:

我使用volatile是因为我的变量是在多线程上下文中使用的,在多线程上下文中,这个变量可以被并发访问/修改,但我可以在没有任何伤害的情况下释放一个加法(参见代码)

我添加了“易失性”,以确保没有出现错误对齐:只读取变量的32位和另一个读取中的其他32位,通过在另一个线程的中间写入可以在2处中断。 我以前的假设(以前的陈述)真的会发生吗?如果不是,是否仍然需要使用“volatile”(选项属性修改可能发生在任何线程中)

在阅读了前两个答案之后。我想坚持这样一个事实,即代码的编写方式,如果由于并发性,我们错过了一个增量(希望从两个线程中增加,但由于并发性,结果只增加了一个),如果至少变量“\u actualVersion”增加了,这一点并不重要

作为参考,这是我使用它的代码部分。仅当应用程序空闲时才报告保存操作(写入磁盘)

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();
        }
    }
公共抽象类选项库:NotifyPropertyChangedBase
{
私有字符串路径;
易失性私有int_savedVersion=0;
易失性私有int _actualVersion=0;
// ******************************************************************
void options base_PropertyChanged(对象发送方,System.ComponentModel.PropertyChangedEventArgs e)
{
_actualVersion++;
Application.Current.Dispatcher.BeginInvoke(新操作(内部保存)、DispatcherPriority.ApplicationIdle);
}
// ******************************************************************
私有void InternalSave()
{
如果(\u实际版本!=\u保存版本)
{
_savedVersion=\u实际版本;
Save();
}
}
// ******************************************************************
/// 
///保存选项
/// 
私有void Save()
{
使用(XmlTextWriter=新的XmlTextWriter(_路径,null))
{
writer.Formatting=格式化.缩进;
XmlSerializer x=新的XmlSerializer(this.GetType());
x、 连载(作者,本);
writer.Close();
}
}

无论是否使用volatile关键字,InternalSave()方法中的比较和赋值都不是线程安全的。如果要避免使用锁,可以使用CompareExchange()和Increment()方法。关于在使用volatile时是否可以将一个变量拆分为两个32位获取的语句,如果您使用的是大于Int32的对象,则可能会出现这种情况

因此,只要您使用Int32,您就不会对所述内容产生任何问题

然而,正如您在建议的链接中所读到的,volatile只提供弱的保证,我更喜欢锁,以确保安全,因为今天的机器有多个CPU,volatile不保证另一个CPU不会进入并执行意外操作

编辑

您是否考虑过使用Interlocked.Increment?

Volatile不足以确保此代码的安全。您可以将低级锁定与Interlocked.Increment()和Interlocked.CompareExchange()一起使用,但几乎没有理由假设Save()是是线程安全的。看起来它确实试图保存一个正在被工作线程修改的对象

这里强烈指出使用lock,不仅是为了保护版本号,而且是为了防止对象在序列化过程中发生更改。不这样做会导致损坏的存储非常罕见,根本无法解决此问题

我在不确定是否有必要的地方使用了volatile

让我非常清楚地说明这一点:

如果您不完全清楚volatile在C#中的含义,请不要使用它。这是一个锐利的工具,仅供专家使用。如果您无法描述当两个线程读取和写入两个不同的volatile fie时,弱内存模型体系结构允许内存访问的所有可能重新排序是什么如果您不知道如何安全地使用volatile,那么您将犯错误,就像您在这里所做的那样,并且编写一个非常脆弱的程序

我很确定在我的情况下,锁的作用会过大

首先,最好的解决方案是干脆不去那里。如果您不编写尝试共享内存的多线程代码,那么您就不必担心锁定问题,这很难纠正

如果您必须编写共享内存的多线程代码,那么最好的做法是始终使用锁。锁几乎不会过度使用。无争用锁的价格大约为10纳秒。您真的在告诉我,额外的10纳秒会对您的用户产生影响吗?如果是这样,那么您有一个非常大的问题y fast程序和具有异常高标准的用户

如果锁内的代码很昂贵,那么争用锁的价格当然会很高。不要在锁内做昂贵的工作,因此争用的概率很低

<>只有当你有一个“强”演示的< /强>性能问题时,不能通过移除争用来解决,你甚至应该开始考虑一个低锁解决方案。

我添加了“易失性”,以确保没有出现不对中:只读取变量的32位和另一个读取的其他32位,这可以通过另一个在中间写入而在两个中被破坏。