C# Interlocked.CompareExchange与枚举
我正在尝试使用此枚举:C# Interlocked.CompareExchange与枚举,c#,interlocked,C#,Interlocked,我正在尝试使用此枚举: public enum State { Idle, Running, //... } 以下代码未编译,但这正是我想要做的: if (Interlocked.CompareExchange(ref state, State.Running, State.Idle) != State.Idle) { throw new InvalidOperationException("Unable to run - not idle"); } 确定我可
public enum State {
Idle,
Running,
//...
}
以下代码未编译,但这正是我想要做的:
if (Interlocked.CompareExchange(ref state, State.Running, State.Idle) != State.Idle) {
throw new InvalidOperationException("Unable to run - not idle");
}
确定我可以使用int而不是enum并使用属性:
private int state = (int)State.Idle;
public State { get { return (State)state; } }
然后将枚举强制转换为整数:
if (Interlocked.CompareExchange(ref state, (int)State.Running, (int)State.Idle) != (int)State.Idle) {
throw new InvalidOperationException("Unable to run - not idle");
}
但是有没有更好的方法可以做到这一点呢?简单点说,没有:-)
遗憾的是,C.Y./.NET考虑<代码> EnUM < /代码> S作为完全类型,部分与它们的基类型断开连接。每次你试图在
枚举上做一些“花哨”的事情时,你都会遇到一些障碍。可以从IL开始,也可以为此创建一个可以从C#使用的助手方法
使用系统;
运用系统反思;
使用System.Reflection.Emit;
使用系统线程;
静态类CompareExchangeEnumImpl
{
公共代表T dImpl(参考T位置、T值、T比较);
public static readonly dImpl Impl=createCompareeExchangeImpl();
静态dImpl CreateCompareExchangeImpl()
{
var underyingtype=Enum.getunderyingtype(typeof(T));
var dynamicMethod=new dynamicMethod(string.Empty,typeof(T),new[]{typeof(T).MakeByRefType(),typeof(T),typeof(T)});
var ilGenerator=dynamicMethod.GetILGenerator();
ilGenerator.Emit(操作码.Ldarg_0);
ilGenerator.Emit(操作码.Ldarg_1);
ilGenerator.Emit(操作码.Ldarg_2);
ilGenerator.Emit(
操作码,呼叫,
typeof(联锁).GetMethod(
“比较交换”,
BindingFlags.Static | BindingFlags.Public,
无效的
新[]{underyingType.MakeByRefType(),underyingType,underyingType},
空);
ilGenerator.Emit(操作码.Ret);
return(dImpl)dynamicMethod.CreateDelegate(typeof(dImpl));
}
}
公共静态类Interlocatex
{
公共静态T比较交换枚举(参考T位置、T值、T比较对象)
{
返回CompareExchangeEnumImpl.Impl(参考位置、值、比较对象);
}
}
公众谘询委员会
{
X,,
Y
}
静态类程序
{
静态void Main()
{
Foo x=Foo.x;
Foo y=Foo.y;
y=InterlockedEx.CompareExchangeEnum(参考x,y,Foo.x);
控制台写入线(“x:+x”);
控制台写入线(“y:+y”);
}
}
输出:
x: Y
y: X
x:Y
y:X
这只是将参数转发到正确的联锁.Exchange
重载。如果T
不是一个真正的枚举类型,或者它的基础类型没有联锁的.Exchange
重载,那么它会严重失败
至少根据PEVerify,生成的IL是可验证的,这可以通过使用AssemblyBuilder
并将结果保存到文件中进行检查
但是有没有更好的方法呢
我使用类而不是枚举:
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()));
}
}
}
联锁
对枚举的操作没有问题:
public enum State { Idle, Running }
unsafe State CompareExchange(ref State target, State v, State cmp)
{
fixed (State* p = &target)
return (State)Interlocked.CompareExchange(ref *(int*)p, (int)v, (int)cmp);
}
使用System.Runtime.CompilerServices.Unsafe查看我在上的完整答案和讨论
这是一个非常好的深入的相关答案
使用系统;
使用System.Runtime.CompilerServices;
使用系统线程;
公共静态类Interlocatex
{
///
///与的枚举等价
///
公共静态TEnum比较交换(参考TEnum位置、TEnum值、TEnum比较)
其中TEnum:struct,Enum
{
返回不安全的.SizeOf()开关
{
//.NET不支持1字节和2字节的原子操作
//没有通用的硬件支持。
4=>CompareExchange32位(参考位置、值、比较项),
8=>CompareExchange64位(参考位置、值、比较项),
_=>抛出新的NotSupportedException(“只有基础类型为4字节或8字节的枚举才允许与Interlocked一起使用”)
};
静态TEnum CompareExchange 32位(参考TEnum位置、TEnum值、TEnum CompareAnd)
{
int COMARANDRAW=不安全的.As(参考COMARAND);
int valueRaw=不安全的.As(参考值);
ref int locationRaw=参考不安全As(参考位置);
int returnRaw=联锁的比较交换(参考位置RAW、值RAW、比较RAW);
返回不安全。As(参考returnRaw);
}
静态TEnum CompareExchange64位(参考TEnum位置、TEnum值、TEnum CompareAnd)
{
长比较RAW=不安全的As(参考比较);
长值原始=不安全的.As(参考值);
ref long locationRaw=参考不安全As(参考位置);
长返回原始=联锁。比较交换(参考位置原始、值原始、比较原始);
返回不安全。As(参考returnRaw);
}
}
}
你所展示的(将其视为int
和施法)基本上就是我所做的。@MarcGravel:基本上?老实说,这没什么大不了的。就我个人而言,我只会将其保留为enum
,但在您进行交换之前将其强制转换。@James您不能这样做;该字段必须为int
,才能在调用中用作ref
。你不能在ref
@James:这会扼杀使用compareeexchange
@DarthVader的理由为什么不呢?我从来没有理由使用Enum的Interlocked.Exchange
,但我确实有其他情况,在这些情况下,CIL允许使用一种清晰正确的方法来做某事,但C#不允许。在这种情况下,我认为C#不是做这项工作的合适工具,所以我不使用C#。真淘气!但是我喜欢它,并将它直接复制到我的代码中:)现在,你也可以做一个线程。volatieread(myEnum)
?@EugeneBeresovsky当然,我不明白为什么不。我的回答应该很容易适应这个问题。还有很多其他方法也可以添加,我认为将所有这些方法都包含在答案中不会有任何好处。:)如果enum作为一种策略对象控制很多事情,这通常是一个好主意。一个症状是不同pla中枚举上有很多开关
public enum State { Idle, Running }
unsafe State CompareExchange(ref State target, State v, State cmp)
{
fixed (State* p = &target)
return (State)Interlocked.CompareExchange(ref *(int*)p, (int)v, (int)cmp);
}