Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.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#get和set线程中使用联锁是否安全?_C#_Multithreading_Thread Safety_Interlocked - Fatal编程技术网

在C#get和set线程中使用联锁是否安全?

在C#get和set线程中使用联锁是否安全?,c#,multithreading,thread-safety,interlocked,C#,Multithreading,Thread Safety,Interlocked,是否可以通过在属性访问器中使用Interlocked来获取线程安全属性 例如: public class A() { private static long count; public static long Count { get { return Interlocked.Read(ref count); } set { Interlocked

是否可以通过在属性访问器中使用
Interlocked
来获取线程安全属性

例如:

public class A()
{
    private static long count;

    public static long Count
    {
        get
        {
            return Interlocked.Read(ref count);
        }
        set
        {
           Interlocked.Exchange(ref count, value); 
        }
    }
}

当运行上述示例时,
get
set
访问器的执行行为是可线性化的。如果不使用
联锁
get
set
访问器的执行行为介于弱一致性和顺序一致性之间(即,仅保证表现出弱一致性)


当作为64位进程运行时,同样可以通过标记字段
volatile
并使用简单的返回语句和赋值运算符来实现。但是,当作为32位进程运行时,
volatile
64位字段上的操作不能保证是原子的,因此需要使用
联锁的
,以确保原子性。

它不是线程安全的。请尝试以下代码:

long i
{
    get { return Interlocked.Read(ref _i); }
    set { Interlocked.Exchange(ref _i, value); }
}


long _i;

void Main()
{

    Parallel.ForEach(Enumerable.Range(0, 1000_000), 
        //new ParallelOptions { MaxDegreeOfParallelism = 1},
        x=>
    {
        i++;
    });

    i.Dump();
}

当您运行此代码时,答案不是1000_000,而是稍微低一点,证明它不是线程安全的。不确定为什么会发生这种情况

“线程安全”是整个程序的属性,而不仅仅是其中的一个片段,而且在您明确定义“线程安全”的含义之前,它也不是一个特别有意义的术语,这意味着在整个程序中会/不会满足哪些条件。话虽如此,我想说的是,一般来说,看到这样的属性是一种代码味道。这对我来说是一个信号,有人认为某些东西会正常工作,而事实上它不会正常工作,尽管我们需要查看属性的每个用法才能确定。关于示例代码的“线程安全性”,我们唯一能说的是,Count的值实际上是由属性集分配给它的值(如果您用简单的分配和返回替换联锁,则不能说什么)但是,正如Servy所说,如果你不确切地定义你所说的“线程安全”属性,这并不意味着什么。@MaMazav你甚至不能这么说,真的。
可以互锁。Read
返回一个值,将其存储在临时文件中,调用setter,更改值,然后重新调用getter反过来,你甚至不能说getter不返回一个过时的值。增加的内存障碍有助于它不那么过时,并防止来自同一线程的get和set的重新排序,但是当涉及多个线程时,由于将值存储在临时(隐式完成)中,你仍然可以做一些非常有趣的事情在它返回之前。原子性和线程安全是两个截然不同的概念,从这段代码中得到的原子性保证是非常弱的。您所能做的就是承诺,当您的程序以32位模式运行时,您永远不会读取部分更新的值。@Servy我唯一的声明是,他编写的代码承诺返回值必须是过去分配给它(而不是汉帕桑所说的部分分配)。我不明白如何将值存储在临时文件中会破坏它。那么……这是肯定还是否定?@Henkholtman在这种情况下,这个问题没有意义,因此不能有简单的肯定或否定的答案。从与相关属性交互的外部代码的角度来看,我的答案是准确的。@280Z28当问题不是answ时由于缺乏信息,您应该投票关闭它,而不是给出一个不完整且毫无帮助的答案。@280Z28此时,您已经给出了一些合理的声明,说明了该代码所做的保证。问题是,我们不知道这些保证是否真的足以满足OP的目的,因为没有id再加上大多数人实际上并不理解“弱一致性”和“顺序一致性”等概念正如你所提到的,在我看来,读者根据这个答案判断他们的代码是否有效的概率似乎很低。++postfix将捕获i的值并使其增加1。在i的值增加之前(读取之后),多个线程可以安全地读取i的值。之后,这些不同的线程将“安全地”将相同的值写在i上。