2013-07-26 6 views
5

मेरे पास "++" या "-" ऑपरेशन के साथ एक एकल Int32 चर का उपयोग करने वाले एकाधिक थ्रेड हैं।सी # में ++ ऑपरेशन परमाणु है?

क्या हमें इसे नीचे पहुंचने से पहले लॉक करने की आवश्यकता है?

lock (idleAgentsLock) 
    { 
     idleAgents--; //we should place it here. 
    } 

या लॉकिंग नहीं करते तो क्या परिणाम होंगे?

+1

बेहतर सवाल यह है कि * * idleAgents' concurrency सुरक्षित * का उपयोग करता है? – user2246674

+0

संभावित डुप्लिकेट [क्या ++ ऑपरेटर थ्रेड सुरक्षित है?] (Http://stackoverflow.com/questions/4628243/is-the-operator-thread-safe) – nawfal

उत्तर

6

यह "परमाणु" नहीं है, बहु-थ्रेडेड अर्थ में नहीं। हालांकि आपका ताला कम से कम सैद्धांतिक रूप से ऑपरेशन की रक्षा करता है। संदेह में जब आप निश्चित रूप से Interlocked.Decrement का उपयोग कर सकते हैं।

5

नहीं ++/- परमाणु संचालन नहीं है, हालांकि पूर्णांक और अन्य आदिम समय को पढ़ना और लिखना एक परमाणु ऑपरेशन माना जाता है।

इन कड़ियों देखें:

इसके अलावा इस blog post जिस तरह से आप के लिए कुछ रुचि का हो।

मैं +12/- ऑपरेटरों के परमाणु कार्यान्वयन के लिए Interlocked कक्षा में Interlocked.Increment का सुझाव दूंगा।

1

यह मशीन आर्किटेक्चर पर निर्भर करता है। आम तौर पर, नहीं, संकलक लोड निर्देश उत्पन्न कर सकता है, मूल्य में वृद्धि/कमी कर सकता है और फिर उसे स्टोर कर सकता है। तो, अन्य धागे वास्तव में उन परिचालनों के बीच मूल्य पढ़ सकते हैं।

अधिकांश CPU निर्देश सेटों में इस उद्देश्य के लिए एक विशेष परमाणु परीक्षण & सेट निर्देश है। मान लीजिए कि आप अपने सी # कोड में असेंबली निर्देशों को एम्बेड नहीं करना चाहते हैं, तो अगला सबसे अच्छा तरीका पारस्परिक बहिष्करण का उपयोग करना है, जो आपने दिखाया है। उस तंत्र का कार्यान्वयन अंततः एक निर्देश का उपयोग करता है जो म्यूटेक्स को लागू करने के लिए परमाणु है (या जो भी इसका उपयोग करता है)।

संक्षेप में: हाँ, आपको आपसी बहिष्करण सुनिश्चित करना चाहिए।

इस उत्तर के दायरे से परे, साझा डेटा प्रबंधित करने के लिए अन्य तकनीकें हैं जो उचित हो सकती हैं या आपकी स्थिति के डोमेन तर्क के आधार पर नहीं हो सकती हैं।

1

एक असुरक्षित वेतन वृद्धि/कमी धागा सुरक्षित नहीं है - और इस तरह परमाणु नहीं धागे के बीच। (हालांकि यह "परमाणु" हो सकता है WRT वास्तविक आईएल/एमएल बदलना।)

यह LINQPad उदाहरण कोड अप्रत्याशित परिणाम दिखाता है:

void Main() 
{ 
    int nWorkers = 10; 
    int nLoad = 200000; 
    int counter = nWorkers * nLoad; 
    List<Thread> threads = new List<Thread>(); 
    for (var i = 0; i < nWorkers; i++) { 
     var th = new Thread((_) => { 
      for (var j = 0; j < nLoad; j++) { 
       counter--; // bad 
      } 
     }); 
     th.Start(); 
     threads.Add(th); 
    } 
    foreach (var w in threads) { 
     w.Join(); 
    } 
    counter.Dump(); 
} 

ध्यान दें कि धागे के बीच दृश्यता महत्व की है । सिंक्रनाइज़ेशन परमाणुता के अलावा इस दृश्यता की गारंटी देता है।

यह कोड कम से कम सीमित संदर्भ में प्रस्तुत किया गया है, आसानी से तय किया गया है।घटती बाहर स्विच और परिणामों पर नज़र डालें: यहां तक ​​कि जब एक volatile चर का उपयोग कर

counter--;       // bad 
Interlocked.Decrement(ref counter); // good 
lock (threads) { counter--; }  // good 

, परिणाम अभी भी अप्रत्याशित हैं। ऐसा लगता है कि (कम से कम यहां, जब मैंने इसे अभी चलाया) यह भी एक परमाणु ऑपरेटर के रूप में प्रतिस्पर्धी धागे के पढ़ने/ओप/लिखने के रूप में interleaved थे। उस व्यवहार अभी भी गलत जब दृश्यता मुद्दों हटा दिया जाता है यह देखने के लिए (वे कर रहे हैं?),

class x { 
    public static volatile int counter; 
} 

जोड़ सकते हैं और स्थानीय counter चर के बजाय x.counter उपयोग करने के लिए उपरोक्त कोड संशोधित।

संबंधित मुद्दे