[संपादित करें:]विदेश मंत्रालय culpa और क्षमा याचना @AnorZaken के बाद से मेरा उत्तर उसकी के समान है। मैं ईमानदारी से मेरा पोस्ट करने से पहले इसे नहीं देखा था। यदि मैं अपने टेक्स्ट और स्पष्टीकरण उपयोगी हैं या अतिरिक्त अंतर्दृष्टि हैं, तो मैं इसे अभी रखूंगा, लेकिन पूर्व कार्य के लिए क्रेडिट ठीक से अनोर को जाता है।
हालांकि मैं इस पृष्ठ पर another solution है, कुछ लोगों को एक पूरी तरह से अलग दृष्टिकोण में रुचि हो सकती। नीचे, मैंने DynamicMethod
जो किसी भी 32- या 64-बिट blittable प्रकार है, जो किसी भी कस्टम Enum
प्रकार, आदिम प्रकार है कि निर्मित विधि भूल गया (uint
, ulong
) भी शामिल है के लिए Interlocked.CompareExchange
लागू करता है, और यहां तक कि अपने खुद ValueType
उदाहरणों - जब तक कि इनमें से किसी भी DWORD (4-बाइट, यानी, int
, System.Int32
) या QWORD (8 बाइट्स, long
, System.Int64
) आकार के होते हैं।
enum ByteSizedEnum : byte { Foo } // no: size is not 4 or 8 bytes
क्रम-उत्पन्न आईएल के सबसे DynamicMethod कार्यान्वयन के साथ के रूप में, सी # कोड प्रतिसाद नहीं: उदाहरण के लिए, Enum
प्रकार नहीं होगा काम के बाद से यह एक गैर डिफ़ॉल्ट आकार, byte
निर्दिष्ट करता है निम्नलिखित देखने के लिए सुंदर नहीं है, लेकिन कुछ लोगों के लिए सुरुचिपूर्ण आईएल और चिकना जिटेटेड देशी कोड इसके लिए तैयार है। उदाहरण के लिए, मैंने पोस्ट की गई अन्य विधि के विपरीत, यह unsafe
सी # कोड का उपयोग नहीं करता है।
कॉल स्थल पर सामान्य प्रकार का स्वत: अनुमान, मैं एक static
कक्षा में सहायक लपेट की अनुमति देना:
public static class IL<T> where T : struct
{
// generic 'U' enables alternate casting for 'Interlocked' methods below
public delegate U _cmp_xchg<U>(ref U loc, U _new, U _old);
// we're mostly interested in the 'T' cast of it
public static readonly _cmp_xchg<T> CmpXchg;
static IL()
{
// size to be atomically swapped; must be 4 or 8.
int c = Marshal.SizeOf(typeof(T).IsEnum ?
Enum.GetUnderlyingType(typeof(T)) :
typeof(T));
if (c != 4 && c != 8)
throw new InvalidOperationException("Must be 32 or 64 bits");
var dm = new DynamicMethod(
"__IL_CmpXchg<" + typeof(T).FullName + ">",
typeof(T),
new[] { typeof(T).MakeByRefType(), typeof(T), typeof(T) },
MethodInfo.GetCurrentMethod().Module,
false);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // ref T loc
il.Emit(OpCodes.Ldarg_1); // T _new
il.Emit(OpCodes.Ldarg_2); // T _old
il.Emit(OpCodes.Call, c == 4 ?
((_cmp_xchg<int>)Interlocked.CompareExchange).Method :
((_cmp_xchg<long>)Interlocked.CompareExchange).Method);
il.Emit(OpCodes.Ret);
CmpXchg = (_cmp_xchg<T>)dm.CreateDelegate(typeof(_cmp_xchg<T>));
}
};
तकनीकी तौर पर, ऊपर आप सभी की जरूरत है। अब आप CmpXchgIL<T>.CmpXchg(...)
पर किसी उचित मूल्य प्रकार (जैसा कि ऊपर दिए गए परिचय में चर्चा की गई है) पर कॉल कर सकते हैं, और यह System.Threading
में अंतर्निहित Interlocked.CompareExchange(...)
जैसा व्यवहार करेगा।
struct XY
{
public XY(int x, int y) => (this.x, this.y) = (x, y); // C#7 tuple syntax
int x, y;
static bool eq(XY a, XY b) => a.x == b.x && a.y == b.y;
public static bool operator ==(XY a, XY b) => eq(a, b);
public static bool operator !=(XY a, XY b) => !eq(a, b);
}
अब आप atomically 64-बिट struct बस के रूप में आप किसी भी CmpXchg संचालन के साथ उम्मीद करेंगे प्रकाशित कर सकते हैं: उदाहरण के लिए, यदि आप एक struct
युक्त दो पूर्णांकों है कहते हैं की सुविधा देता है। यह परमाणु रूप से दो पूर्णांक प्रकाशित करता है ताकि किसी अन्य धागे के लिए 'टूटा' या असंगत जोड़ी देखना असंभव हो। कहने की जरूरत नहीं है, लॉजिकल जोड़ी के साथ आसानी से ऐसा करना समवर्ती प्रोग्रामिंग में बेहद उपयोगी है, और यदि आप एक विस्तृत संरचना तैयार करते हैं जो उपलब्ध 64 (या 32) बिट्स में कई फ़ील्ड पैक करता है। यहाँ ऐसा करने के लिए कॉल-साइट के एक उदाहरण है:
var xy = new XY(3, 4); // initial value
//...
var _new = new XY(7, 8); // value to set
var _exp = new XY(3, 4); // expected value
if (IL<XY>.CmpXchg(ref xy, _new, _exp) != _exp) // atomically swap the 64-bit ValueType
throw new Exception("change not accepted");
ऊपर, मैंने कहा कि आप ऐसा है कि आप जेनेरिक पैरामीटर निर्दिष्ट करने की जरूरत नहीं प्रकार निष्कर्ष को सक्षम करने से कॉल साइट को साफ़ रखने कर सकते हैं। ऐसा करने के लिए, बस अपने गैर सामान्य वैश्विक वर्गों में से एक में एक स्थिर सामान्य विधि को परिभाषित:
public static class my_globals
{
[DebuggerStepThrough, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T CmpXchg<T>(ref T loc, T _new, T _old) where T : struct =>
_IL<T>.CmpXchg(ref loc, _new, _old);
}
मैं इस बार एक Enum
का उपयोग कर एक अलग उदाहरण के साथ सरलीकृत कॉल साइट दिखाता हूँ,:
using static my_globals;
public enum TestEnum { A, B, C };
static void CompareExchangeEnum()
{
var e = TestEnum.A;
if (CmpXchg(ref e, TestEnum.B, TestEnum.A) != TestEnum.A)
throw new Exception("change not accepted");
}
मूल प्रश्न का सवाल है, ulong
और uint
काम तुच्छता के साथ-साथ:
ulong ul = 888UL;
if (CmpXchg(ref ul, 999UL, 888UL) != 888UL)
throw new Exception("change not accepted");
मैं वास्तव में यह स्वीकार्य उत्तर देने जा रहा हूं क्योंकि मैं इस तरह UIU3232 के लिए तुलना एक्सचेंज लिख सकता हूं, जो मूल प्रश्न था। – Martin
यह काम जानना अच्छा लगता है, लेकिन मुझे लगता है कि आपके "अपने जोखिम पर उपयोग" चेतावनी बहुत दृढ़ता से जोर दिया जाना चाहिए। यदि कंपाइलर के अगले संस्करण ने यहां एक अस्थायी उत्पन्न किया है, तो यह चुपचाप एक बग पेश करेगा जो ट्रैक करने में बहुत मुश्किल हो सकता है। मैं एक बड़ा यूनिट-परीक्षक नहीं हूं, लेकिन इस तरह की चाल मूल रूप से एक की मांग करती है। फिर भी, के बारे में जानने के लिए एक अच्छी बात है। ऊपर मतदान किया। – Gabriel
मैंने इसका परीक्षण नहीं किया है, लेकिन मुझे संदेह है कि डायनामिक मोड और आईएल-पीढ़ी का उपयोग करके 'असुरक्षित' के बिना यह संभव है। (चूंकि यह enums के लिए संभव है, [जैसा कि यहां दिखाया गया है] (http://stackoverflow.com/a/18359360/533837)।) यह अभी भी एक "हैक" है, लेकिन मुझे नहीं लगता कि यह कंपाइलर कार्यान्वयन पर निर्भर करेगा । – AnorZaken