如何为 C# 中的枚举类型应用 InterLocked.Exchange?

Posted

技术标签:

【中文标题】如何为 C# 中的枚举类型应用 InterLocked.Exchange?【英文标题】:How to apply InterLocked.Exchange for Enum Types in C#? 【发布时间】:2011-08-24 14:28:56 【问题描述】:
public enum MyEnumValue1, Value2  
class MyClass 
 
    private MyEnum _field;   
    public MyEnum Field  // added for convenience
    
        get  return _field;   
        set  Interlocked.Exchange(ref _field, value); // ERROR CS0452  
      
 

可以解决:

 public enum MyEnumValue1, Value2  
 public class MyClass2  
   
   private int _field;  //change to int
   public MyEnum Field  // added for convenience
    
    get  return (MyEnum)_field; 
    set  System.Threading.Interlocked.Exchange(ref _field, (int)value); 
     
 

有没有更好的办法解决这个问题?

【问题讨论】:

Interlocked.Exchange 有一个通用的重载,那不行吗? @harold:不,重载有一个约束,将 T 限制为引用类型。 既然你忽略了Exchange的返回值,那么它在这里添加了什么值? 是的,没有想到。无论如何,交换的结果没有被使用,所以它不是真正的交换,而只是一个分配。制作枚举字段volatile 应该就够了吧? 【参考方案1】:

有没有更好的办法解决这个问题?

如果你需要使用Interlocked.Exchange 那么这是最好的方法,事实上我认为这是交换枚举的唯一方法。

你得到编译器错误的原因是编译器认为你想使用Exchange<T>,但是 T 需要是一个引用类型才能工作,因为你没有使用它失败的引用类型。因此,最好的解决方法是像您所做的那样强制转换为int,从而强制编译器使用非泛型Exchange(int, int)

【讨论】:

是的。关于 Interlocked 的使用、编译器错误的原因以及通过强制转换解决的方法,我完全同意您的看法。我只是觉得代码看起来很难看。 美在旁观者的眼中,我不认为它看起来那么糟糕:) 至少“丑陋”的代码对只看到 MyEnum 属性的客户是隐藏的。我很好奇您在这里实际上在做什么-您是否阅读了其他答案?至少可以说,忽略返回值似乎很“奇怪”。 好吧,我不需要返回值。与 Tejs 建议的锁定方法相比,Interlocked 只是优化。我很可能会采取 lock(syncobject) 方法来解决我当前的问题。谢谢您的回复。 接受您对我的接受百分比的回答非常低,因此许多人对我的问题投了反对票。谢谢你的回答。【参考方案2】:

您似乎不需要 Interlocked.Exchange 的“交换”功能,因为您忽略了它的返回值。因此,我认为可能让您最开心的解决方案是将 _field 标记为 volatile:

private volatile MyEnum _field;

【讨论】:

AFAIK"volatile" 不会取代 Interlocked.Exchange!它只是确保变量没有被缓存,而是直接使用。增加一个变量实际上需要三个操作:1.read 2.copy 3.write Interlocked.Exchange 将所有三个部分作为单个原子操作执行 volatile 在 C# 中会导致完整的内存栅栏,而在 C++ 中它几乎没用。而你在这里只做一个操作:写。确实,如果您进行了读/修改/写操作,volatile 就不会删除它。 @harold:C# 中的易失性被记录为在读取和写入时导致半栅栏,而不是全栅栏。在实践中,实现当然可以选择提供超过一半的栅栏,但是如果您依赖它,那么您依赖的是可能会更改的未记录的实现细节。也就是说,您当然正确地注意到“volatile”和“atomic test and set”是两个完全不同的东西。 @harold:你是否在一个具有强内存模型的架构上进行反汇编,比如 x64 或 x86?在那些架构上,所有访问都已经被视为易失性,因此不需要生成栅栏。 “volatile”对这些架构的唯一影响是防止编译器优化。如果您想看到生成的栅栏,请在像安腾这样的弱内存模型芯片上进行拆卸。 @harold:Volatile 不保证 C# 中的顺序一致性。它保证获取和释放语义。如果您想知道 C# 究竟保证了什么,我鼓励您阅读 C# 规范的相关部分。【参考方案3】:

Interlocked 方法很好。您可以使用普通的旧lock,但这似乎有点过分了。但是,您将需要在 getter 中使用某种保护读取,否则您可能会遇到内存屏障问题。由于您已经在 setter 中使用了 Interlocked 方法,因此在 getter 中执行相同操作是有意义的。

public MyEnum Field  // added for convenience
 
  get  return (MyEnum)Interlocked.CompareExchange(ref _field, 0, 0); 
  set  Interlocked.Exchange(ref _field, (int)value); 
  

如果您愿意,也可以将该字段标记为 volatile

【讨论】:

【参考方案4】:

有没有更好的办法解决这个问题?

我使用类而不是枚举:

public class DataCollectionManagerState

    public static readonly DataCollectionManagerState Off = new DataCollectionManagerState()  ;
    public static readonly DataCollectionManagerState Starting = new DataCollectionManagerState()  ;
    public static readonly DataCollectionManagerState On = new DataCollectionManagerState()  ;

    private DataCollectionManagerState()  

    public override string ToString()
    
        if (this == Off) return "Off";
        if (this == Starting) return "Starting";
        if (this == On) return "On";

        throw new Exception();
    


public class DataCollectionManager

    private static DataCollectionManagerState _state = DataCollectionManagerState.Off;

    public static void StartDataCollectionManager()
    
        var originalValue = Interlocked.CompareExchange(ref _state, DataCollectionManagerState.Starting, DataCollectionManagerState.Off);
        if (originalValue != DataCollectionManagerState.Off)
        
            throw new InvalidOperationException(string.Format("StartDataCollectionManager can be called when it's state is Off only. Current state is \"0\".", originalValue.ToString()));
        

        // Start Data Collection Manager ...

        originalValue = Interlocked.CompareExchange(ref _state, DataCollectionManagerState.On, DataCollectionManagerState.Starting);
        if (originalValue != DataCollectionManagerState.Starting)
        
            // Your code is really messy
            throw new Exception(string.Format("Unexpected error occurred. Current state is \"0\".", originalValue.ToString()));
        
    

【讨论】:

【参考方案5】:

为什么不简单地同步线程?

protected static object _lockObj = new object();

set

    lock(_lockObj)
    
         _field = value;
    

【讨论】:

MSDN 说,在现代 CPU 的 Interlocked 上进行了优化,通常实际上是单个 CPU 指令。 这仅在对字段的每次访问都被锁定时才有效,这可能会导致大量工作。

以上是关于如何为 C# 中的枚举类型应用 InterLocked.Exchange?的主要内容,如果未能解决你的问题,请参考以下文章

如何为文件类型创建所有文件关联(友好的应用程序名称和可执行文件)的 C# 列表

C# InterLock保证数据一致性

自定义枚举作为 C# 中的应用程序设置类型?

如何为子目录中的 C# 文件编写 DependentUpon 子句?

如何为 Spring Security 创建类型安全的用户角色?

如何区分interlock和jersey