पर केवल एक ही लिखने के साथ मैं दो तरीकों के साथ एक वर्ग बनाने के लिए करना चाहते हैं:पढ़ें एक अप-टू एक Interlocked चर से दिनांक मान, चर
void SetValue(T value)
भंडार एक मूल्य है, लेकिन केवल एक ही भंडारण की अनुमति देता है मूल्य (अन्यथा यह एक अपवाद फेंकता है)।T GetValue()
मान प्राप्त करता है (और अगर कोई मूल्य अभी तक है एक अपवाद फेंकता है)।- मूल्य पढ़ना सस्ता होना चाहिए:
मैं निम्नलिखित इच्छाओं/कमी की है।
- मूल्य लिखना (मामूली) महंगा हो सकता है।
GetValue()
अपवाद फेंक चाहिए तभी जब अप-टू-डेट मूल्य अनुपस्थित (null
) है: यह एक और धागा मेंSetValue()
के लिए एक कॉल के बाद एक बासीnull
मूल्य के आधार पर एक अपवाद फेंक नहीं करना चाहिए।- मूल्य केवल एक बार लिखा जाता है। इसका मतलब है कि
GetValue()
को मान को ताज़ा करने की आवश्यकता नहीं है यदि यह शून्य नहीं है। - यदि एक पूर्ण मेमोरी बाधा से बचा जा सकता है, तो यह (अधिक) बेहतर है।
- मुझे लगता है कि ताला मुक्त संगामिति बेहतर है मिलता है, लेकिन मुझे यकीन है कि अगर यह मामला यहाँ है नहीं कर रहा हूँ।
मैं इसे प्राप्त करने के कई तरीकों से आया हूं, लेकिन मुझे यकीन नहीं है कि कौन सा सही है, जो कुशल हैं, वे सही (और) कुशल क्यों हैं, और यदि कोई बेहतर तरीका है जो मैं चाहता हूं उसे प्राप्त करने के लिए।
विधि 1
- क्षेत्र
- क्षेत्र से पढ़ने के लिए
Interlocked.CompareExchange
का उपयोग करने के लिए लिखने के लिए एक गैर अस्थिर क्षेत्र Interlocked.CompareExchange
का उपयोग का उपयोग करना- यह (संभवतः गलत) धारणा पर निर्भर करता है कि किसी क्षेत्र पर
Interlocked.CompareExchange(ref v, null, null)
करने के बाद परिणामस्वरूप अगले मूल्यों को प्राप्त करने के परिणामस्वरूप कम से कमInterlocked.CompareExchange
देखा गया होगा।
कोड:
public class SetOnce1<T> where T : class
{
private T _value = null;
public T GetValue() {
if (_value == null) {
// Maybe we got a stale value (from the cache or compiler optimization).
// Read an up-to-date value of that variable
Interlocked.CompareExchange<T>(ref _value, null, null);
// _value contains up-to-date data, because of the Interlocked.CompareExchange call above.
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
}
// _value contains up-to-date data here too.
return _value;
}
public T SetValue(T newValue) {
if (newValue == null) {
throw new System.ArgumentNullException();
}
if (Interlocked.CompareExchange<T>(ref _value, newValue, null) != null) {
throw new System.Exception("Value already present.");
}
return newValue;
}
}
विधि 2
- एक
volatile
क्षेत्र - का उपयोग का उपयोग करना Ìnterlocked.CompareExchange
to write the value (with [Joe Duffy](http://www.bluebytesoftware.com/blog/PermaLink,guid,c36d1633-50ab-4462-993e-f1902f8938cc.aspx)'s
#pragmato avoid the compiler warning on passing a volatile value by
ref`)। - मूल्य पढ़ना सीधे, के बाद से क्षेत्र
volatile
कोड है:
public class SetOnce2<T> where T : class
{
private volatile T _value = null;
public T GetValue() {
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
return _value;
}
public T SetValue(T newValue) {
if (newValue == null) {
throw new System.ArgumentNullException();
}
#pragma warning disable 0420
T oldValue = Interlocked.CompareExchange<T>(ref _value, newValue, null);
#pragma warning restore 0420
if (oldValue != null) {
throw new System.Exception("Value already present.");
}
return newValue;
}
}
विधि 3
- एक गैर अस्थिर क्षेत्र का उपयोग करना।
- लिखते समय लॉक का उपयोग करना।
- पढ़ते समय लॉक का उपयोग करना अगर हम शून्य पढ़ते हैं (हमें लॉक के बाहर एक सुसंगत मूल्य मिलेगा क्योंकि संदर्भ परमाणु रूप से पढ़ा जाता है)।
कोड:
public class SetOnce3<T> where T : class
{
private T _value = null;
public T GetValue() {
if (_value == null) {
// Maybe we got a stale value (from the cache or compiler optimization).
lock (this) {
// Read an up-to-date value of that variable
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
return _value;
}
}
return _value;
}
public T SetValue(T newValue) {
lock (this) {
if (newValue == null) {
throw new System.ArgumentNullException();
}
if (_value != null) {
throw new System.Exception("Value already present.");
}
_value = newValue;
return newValue;
}
}
}
विधि 4
- एक अस्थिर क्षेत्र
- एक ताला का उपयोग कर मूल्य लेखन का उपयोग करना।
- सीधे मूल्य पढ़ना, क्योंकि क्षेत्र अस्थिर है (अगर हम लॉक का उपयोग नहीं करते हैं तो भी हम एक सुसंगत मूल्य प्राप्त करेंगे क्योंकि संदर्भ परमाणु रूप से पढ़ा जाता है)।
कोड:
public class SetOnce4<T> where T : class
{
private volatile T _value = null;
public T GetValue() {
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
return _value;
}
public T SetValue(T newValue) {
lock (this) {
if (newValue == null) {
throw new System.ArgumentNullException();
}
if (_value != null) {
throw new System.Exception("Value already present.");
}
_value = newValue;
return newValue;
}
}
}
अन्य तरीकों
मैं भी तकनीक लेखन से किसी के साथ Thread.VolatileRead()
इस्तेमाल कर सकते हैं मूल्य को पढ़ने के लिए संयोजन में,।
आप एक 'ReaderWriterLock (स्लिम)' का उपयोग कर विचार किया है? क्या आप सुनिश्चित हैं कि एक साधारण 'लॉक' आपकी आवश्यकताओं को पूरा नहीं करता है? क्यों नहीं? 'SetOnce' कक्षा के लिए उपयोग पैटर्न क्या है? – dtb
मैं 'रीडरवाइटर लॉक' के बारे में भूल गया था, मैं इसे लॉक दूंगा। लेकिन नियमित रूप से 'लॉक' की तरह, मुझे लगता है कि यह प्रत्येक पठन पर एक सिंक्रनाइज़ेशन ओवरहेड पेश करेगा, जिसे मैं तब से टालना चाहूंगा क्योंकि मान केवल एक बार बदल गया है, इसलिए जब गैर-शून्य मान (ताले के बिना) पढ़ते हैं, तो किसी के पास गारंटी है कि यह पुराना नहीं है। –
रीडरवाइटर लॉक (स्लिम) वास्तव में बेहतर है तो सामान्य लॉक, क्योंकि यह एकाधिक एक साथ पढ़ने की अनुमति देता है। – Woodman