如何为 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# 文件编写 DependentUpon 子句?