मेरे पास एक वर्ग है जिसमें दो int फ़ील्ड x और y हैं, और एक विधि Increment है जो इन दोनों को बढ़ाता है फ़ील्ड को क्रमशः dx और dy के आधार पर चुनें। मैं अपनी कक्षा की स्थिति को मूक अंकगणितीय अतिप्रवाह (जिसके परिणामस्वरूप x या y या दोनों नकारात्मक हो जाएगा) से दूषित होने से रोकना चाहता हूं, इसलिए मैं स्पष्ट रूप से वृद्धि कर रहा हूं चेक किए गएमें फ़ील्ड ब्लॉक:

class MyClass
{
    private int x;
    private int y;

    public void Increment(int dx, int dy)
    {
        checked { x += dx; y += dy; }
    }
}

यह सुनिश्चित करना चाहिए कि अंकगणितीय अतिप्रवाह के मामले में कॉलर को एक OverflowException, और मेरी कक्षा की स्थिति बरकरार रहेगी। लेकिन तब मुझे एहसास हुआ कि y की वृद्धि में एक अंकगणितीय अतिप्रवाह हो सकता है, जब x पहले ही सफलतापूर्वक बढ़ गया है, जिसके परिणामस्वरूप एक अलग प्रकार का राज्य भ्रष्टाचार हो सकता है, जो नहीं है पहले की तुलना में कम विघटनकारी। इसलिए मैंने Increment पद्धति के कार्यान्वयन को इस तरह बदल दिया:

public void Increment2(int dx, int dy)
{
    int x2, y2;
    checked { x2 = x + dx; y2 = y + dy; }
    x = x2; y = y2;
}

यह समस्या के तार्किक समाधान की तरह लगता है, लेकिन अब मुझे चिंता है कि संकलक मेरे सावधानीपूर्वक तैयार किए गए कार्यान्वयन को "अनुकूलित" कर सकता है, और निर्देशों को इस तरह से पुन: व्यवस्थित कर सकता है जिससे पहले x असाइनमेंट हो सके। y + dy जोड़, जिसके परिणामस्वरूप फिर से भ्रष्टाचार होता है। मैं पूछना चाहता हूं कि क्या सी # विनिर्देश के अनुसार, यह अवांछनीय परिदृश्य संभव है।

मैं checked कीवर्ड को हटाने पर भी विचार कर रहा हूं, और इसके बजाय अपने प्रोजेक्ट को "अंकगणित अतिप्रवाह के लिए जांचें" विकल्प सक्षम (<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>) के साथ संकलित कर रहा हूं। क्या इससे Increment पद्धति के अंदर निर्देशों के संभावित पुन: क्रम में होने के संबंध में कोई फर्क पड़ सकता है?


अपडेट: टपल डीकंस्ट्रक्शन का उपयोग करके अधिक संक्षिप्त अंकगणित-अतिप्रवाह-सुरक्षित कार्यान्वयन संभव है। क्या यह संस्करण वर्बोज़ कार्यान्वयन से अलग (कम सुरक्षित) है?

public void Increment3(int dx, int dy)
{
    (x, y) = checked((x + dx, y + dy));
}

स्पष्टीकरण: MyClass को सिंगल-थ्रेडेड एप्लिकेशन में उपयोग करने का इरादा है। थ्रेड-सुरक्षा चिंता का विषय नहीं है (मुझे पता है कि यह थ्रेड-सुरक्षित नहीं है, लेकिन इससे कोई फर्क नहीं पड़ता)।

1
Theodor Zoulias 5 सितंबर 2021, 09:10

3 जवाब

सबसे बढ़िया उत्तर

टीएल; डॉ; यह एक ही धागे के भीतर करना पूरी तरह से सुरक्षित है।

सीएलआई, जो आपके कोड को देशी मशीनी भाषा में लागू करता है, बस अनुमति नहीं है निर्देशों को इस तरह से फिर से व्यवस्थित करने के लिए कि एक दृश्य दुष्प्रभाव हो, कम से कम एक धागे से टिप्पणियों के रूप में चिंतित। यह विनिर्देश द्वारा निषिद्ध है।

आइए एक नज़र डालते हैं ECMA-335, सीएलआर और सीएलआई के लिए विनिर्देश, (मेरा बोल्ड)

I.12.6.4 अनुकूलन

सीएलआई के अनुरूप कार्यान्वयन किसी भी तकनीक का उपयोग करके प्रोग्राम निष्पादित करने के लिए स्वतंत्र हैं जो गारंटी देता है, निष्पादन के एक थ्रेड के भीतर, कि साइड-इफेक्ट्स और अपवाद एक थ्रेड द्वारा उत्पन्न दिखाई दे रहे हैं < मजबूत>सीआईएल द्वारा निर्दिष्ट क्रम में।
... स्निप ...
किसी अन्य थ्रेड द्वारा थ्रेड में इंजेक्ट किए गए अपवादों के सापेक्ष कोई ऑर्डरिंग गारंटी नहीं है (ऐसे अपवादों को कभी-कभी "एसिंक्रोनस अपवाद" कहा जाता है (जैसे, System.Threading.ThreadAbortException)।

[तर्कसंगत: एक अनुकूल करने वाला कंपाइलर साइड-इफेक्ट्स और सिंक्रोनस अपवादों को इस हद तक पुन: व्यवस्थित करने के लिए स्वतंत्र है कि यह पुनर्क्रमण किसी भी अवलोकन योग्य प्रोग्राम व्यवहार को नहीं बदलतातर्क समाप्त करें]

[नोट: सीएलआई के एक कार्यान्वयन को एक अनुकूलन संकलक का उपयोग करने की अनुमति है, उदाहरण के लिए, सीआईएल को देशी मशीन कोड में परिवर्तित करने के लिए, बशर्ते संकलक (निष्पादन के प्रत्येक एकल धागे के भीतर) को बनाए रखता है एक ही दुष्प्रभावों और समकालिक अपवादों का क्रम
यह आईएसओ सी ++ (जो अनुक्रम बिंदुओं की एक जोड़ी के बीच पुन: व्यवस्थित करने की अनुमति देता है) या आईएसओ योजना (जो कार्यों के लिए तर्कों के पुन: क्रम की अनुमति देता है) की तुलना में एक मजबूत स्थिति है। नोट समाप्त करें]

C# द्वारा संकलित IL कोड में इसलिए अपवाद होना चाहिए निर्दिष्ट क्रम में होना चाहिए, इसलिए यदि checked संदर्भ में कोई अतिप्रवाह होता है, तो अपवाद को पहले फेंक दिया जाना चाहिए अगले निर्देशों का कोई अवलोकन।

ध्यान दें कि इसका मतलब नहीं है कि दो जोड़ स्थानीय चर में स्टोर से पहले नहीं हो सकते हैं, क्योंकि एक बार अपवाद फेंकने के बाद स्थानीय लोगों को नहीं देखा जा सकता है। पूरी तरह से अनुकूलित बिल्ड में, स्थानीय लोगों को संभवतः सीपीयू रजिस्टरों में संग्रहीत किया जाएगा, और अपवाद की स्थिति में मिटा दिया जाएगा।

सीपीयू आंतरिक रूप से पुन: व्यवस्थित करने के लिए भी स्वतंत्र है, जब तक वही गारंटी लागू होती है।


इस सबका एक अपवाद है, उल्लिखित मल्टी-थ्रेडिंग भत्ता को छोड़कर:

ऑप्टिमाइज़र को विधियों में छूट वाले अपवादों के लिए अतिरिक्त अक्षांश प्रदान किया जाता है। एक विधि E है - एक प्रकार के अपवाद के लिए छूट दी जाती है यदि E प्रकार के अपवादों से संबंधित अंतरतम कस्टम विशेषता System.Runtime.CompilerServices. CompilationRelaxationsAttribute मौजूद है और प्रकार के अपवादों को शिथिल करने के लिए निर्दिष्ट करती है .

हालाँकि वर्तमान Microsoft कार्यान्वयन वैसे भी इस तरह का छूट विकल्प प्रदान नहीं करता है।


टपल-डिकंस्ट्रक्टिंग सिंटैक्स का उपयोग करने के संबंध में, दुर्भाग्य से C# 7 के लिए विनिर्देश जारी नहीं किया गया है, लेकिन Github पर यह पेज इंगित करता है कि यह साइड-इफ़ेक्ट मुक्त भी होना चाहिए।

2
Charlieface 5 सितंबर 2021, 08:54

अपनी कक्षा को अपरिवर्तनीय बनाएं। जब आप कुछ बदलना चाहते हैं, तो एक नया उदाहरण लौटाएं।

class MyClass
{
    private int x;
    private int y;

    public MyClass Increment(int dx, int dy)
    {
        checked
        {
            return new MyClass { x = this.x + dx, y = this.y + dy }; 
        }
    }
}

और अपने कॉलिंग कोड में, आप प्रतिस्थापित करेंगे

myClass.Increment( a, b );

साथ

myClass = myClass.Increment( a, b );

यह सुनिश्चित करता है कि आपकी कक्षा हमेशा आंतरिक रूप से सुसंगत है।

यदि आप एक नया उदाहरण वापस नहीं करना चाहते हैं, तो आप आंतरिक रीडोनली संरचना का उपयोग करके समान लाभ प्राप्त कर सकते हैं।

public readonly struct Coords
{
    public int X { get; init; }
    public int Y { get; init; }
}

class MyClass
{
    private Coords _coords;

    public void Increment(int dx, int dy)
    {
        checked
        { 
            var newValue = new Coords { X = _coords.X + dx, Y = _coords.Y + dy };
        }
        _coords = newValue;
    }
}
2
John Wu 5 सितंबर 2021, 07:29

यदि आप पुनर्क्रमण को रोकना चाहते हैं तो मेमोरीबैरियर विधि का उपयोग करना चाहिए।

इस लिंक का प्रयोग करें: https: //docs.microsoft.com/en-us/dotnet/api/system.threading.thread.memorybarrier?redirectedfrom=MSDN&view=net-5.0#System_Threading_Thread_MemoryBarrier

-1
mostafa 5 सितंबर 2021, 06:25