c# - 线程安全的通用字段

标签 c# .net thread-safety lockless

我有一个通用字段和一个封装它的属性:

T item;

public T Item
{
    get { return item; }
    set { item = value; }
}

问题是这个属性可以从一个线程写入并同时从多个线程读取。如果Tstruct , 或 long ,读者可能会得到部分旧值和部分新值的结果。我怎样才能防止这种情况发生?

我尝试使用 volatile ,但这是不可能的:

A volatile field cannot be of the type 'T'.

因为这是我已经编写的更简单的代码案例,它使用 ConcurrentQueue<T> ,我也想过在这里使用它:

ConcurrentQueue<T> item;

public T Item
{
    get
    {
        T result;
        item.TryPeek(out result);
        return item;
    }

    set
    {
        item.TryEnqueue(value);
        T ignored;
        item.TryDequeue(out ignored);
    }
}

这会奏效,但在我看来,对于本应简单的事情来说,这是一种过于复杂的解决方案。

性能很重要,因此应尽可能避免锁定。

如果 setget 同时发生,我不在乎是否get返回旧值或新值。

最佳答案

这完全取决于类型,T

如果您能够在 T 上放置一个 class 约束,那么在这种特殊情况下您不需要做任何事情Reference assignments are atomic .这意味着您不能对基础变量进行部分或损坏的写入。

阅读也是如此。您将无法阅读部分编写的引用。

如果 T 是一个结构,那么只有以下结构可以被原子地读取/分配(根据 C# 规范的第 12.5 节,强调我的,也证明了上述声明):

Reads and writes of the following data types shall be atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.

所以如果你所做的只是尝试读/写,并且你满足上述条件之一,那么你不必做任何事情(但这意味着你还必须对类型进行约束T).

如果您不能保证 T 上的约束,那么您将不得不求助于 lock statement 之类的东西同步访问(如前所述,用于读取和写入)。

如果您发现使用 lock 语句(实际上是 Monitor class )会降低性能,那么您可以使用 SpinLock structure ,因为它旨在帮助 Monitor 太重的地方:

T item;

SpinLock sl = new SpinLock();

public T Item
{
    get 
    { 
        bool lockTaken = false;

        try
        {
            sl.Enter(ref lockTaken);
            return item; 
        }
        finally
        {
            if (lockTaken) sl.Exit();
        }
    }
    set 
    {
        bool lockTaken = false;

        try
        {
            sl.Enter(ref lockTaken);
            item = value;
        }
        finally
        {
            if (lockTaken) sl.Exit();
        }
    }
}

但是,要小心,因为the performance of SpinLock can degrade and will be the same as the Monitor class if the wait is too long ;当复制语义)。

当然,你应该自己测试一下你预测这个类将被使用的情况,看看哪种方法最适合你(lockSpinLock结构)。

关于c# - 线程安全的通用字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11544384/

相关文章:

java - 线程安全和不变的关系

java - 使用 foreach 遍历 ArrayList 时的线程安全

c# - 在事件接收器中获取 SharePoint ListItem 的 After 和 Before 属性

c# - Azure powershell 无法识别 "azure"

c# - 反转数组中没有第一个的所有单词

c# - 为什么 C# 编译器不能将文字负值转换为枚举?

Python队列链接对象运行异步协程与主线程输入

c# - StyleCop 格式

c# - 如何将 Json 字符串转换为 C# Class 对象?

c# - 空传播运算符