सी # थ्रेड सुरक्षित फास्ट (स्था) काउंटर


147

सर्वोत्तम संभव प्रदर्शन के साथ C # में थ्रेड सेफ काउंटर प्राप्त करने का तरीका क्या है?

यह उतना ही सरल है जितना कि यह मिलता है:

public static long GetNextValue()
{
    long result;
    lock (LOCK)
    {
        result = COUNTER++;
    }
    return result;
}

लेकिन वहाँ तेजी से विकल्प हैं?

जवाबों:



108

जैसा कि दूसरों ने सुझाया है, इससे Interlocked.Incrementबेहतर प्रदर्शन होगा lock()। बस IL और असेंबली पर एक नज़र डालें जहां आप देखेंगे कि Increment"बस लॉक" स्टेटमेंट में बदल जाता है और इसका वेरिएबल सीधे (x86) या "जोड़ा गया" (x64) हो जाता है।

यह "बस लॉक" स्टेटमेंट किसी अन्य सीपीयू को बस तक पहुंचने से रोकने के लिए बस को लॉक करता है जबकि कॉलिंग सीपीयू अपना ऑपरेशन करता है। अब, C # lock()स्टेटमेंट के IL पर एक नज़र डालें । यहां आपको Monitorकिसी अनुभाग को शुरू करने या समाप्त करने के लिए कॉल दिखाई देगी ।

दूसरे शब्दों में, .Net lock()बयान .Net की तुलना में बहुत अधिक कर रहा है Interlocked.Increment

एसओ, यदि आप सब करना चाहते हैं तो एक परिवर्ती वृद्धि Interlock.Incrementहोगी , और तेज़ होगी। उपलब्ध विभिन्न परमाणु संचालन को देखने के लिए और अपनी आवश्यकताओं के अनुरूप उन लोगों को खोजने के लिए सभी इंटरलॉक की गई विधियों की समीक्षा करें। का प्रयोग करें lock()जब आप उन संसाधनों पूर्णांकों की तुलना में अधिक जटिल हैं करने के लिए कई परस्पर संबंधित वेतन वृद्धि / decrements, या क्रमानुसार करने पहुँच की तरह अधिक जटिल चीज़ों करना चाहते हैं।


3
-1 कार्यान्वयन विवरण के लिए। यह सच है कि लॉकिंग एक परमाणु ऑप की तुलना में धीमी है, लेकिन इसका आईएल से कोई लेना-देना नहीं है। यदि उनके शब्दार्थों के लिए यह फ़ंक्शन कॉल एक परमाणु ऑप की तुलना में तेज़ी से होगा, जो स्वाभाविक रूप से आईएल की आवश्यकता नहीं है।
पिल्ला

33

मेरा सुझाव है कि आप System.Threading पुस्तकालय में .NET के इंटरलॉक वेतन वृद्धि का उपयोग करें।

निम्नलिखित कोड संदर्भ द्वारा एक लंबा चर बढ़ाएगा और पूरी तरह से सुरक्षित है:

Interlocked.Increment(ref myNum);

स्रोत: http://msdn.microsoft.com/en-us/library/dd78zt0c.aspx



1

जैसा कि पहले ही उल्लेख किया गया है Interlocked.Increment

MS से कोड उदाहरण:

निम्न उदाहरण से पता चलता है कि 0 से 1,000 तक की कितनी यादृच्छिक संख्याओं को एक मिडपॉइंट मान के साथ 1,000 यादृच्छिक संख्याएं उत्पन्न करने के लिए आवश्यक है। मिडपॉइंट मानों की संख्या पर नज़र रखने के लिए, एक चर, मिडपॉइंटकाउंट, 0 के बराबर सेट किया गया है और हर बार वृद्धि होने पर यादृच्छिक संख्या जनरेटर एक मिडपॉइंट मान लौटाता है जब तक कि यह 10,000 तक नहीं पहुंचता। क्योंकि तीन थ्रेड यादृच्छिक संख्याएँ उत्पन्न करते हैं, इंक्रीमेंट (Int32) विधि को यह सुनिश्चित करने के लिए कहा जाता है कि एकाधिक थ्रेड्स मध्य बिंदु को समवर्ती रूप से अपडेट नहीं करते हैं। ध्यान दें कि यादृच्छिक संख्या जनरेटर की सुरक्षा के लिए एक लॉक का भी उपयोग किया जाता है, और यह सुनिश्चित करने के लिए कि एक उलटी गिनती वस्तु का उपयोग किया जाता है ताकि मुख्य विधि तीन थ्रेड्स से पहले निष्पादन को समाप्त न करें।

using System;
using System.Threading;

public class Example
{
   const int LOWERBOUND = 0;
   const int UPPERBOUND = 1001;

   static Object lockObj = new Object();
   static Random rnd = new Random();
   static CountdownEvent cte;

   static int totalCount = 0;
   static int totalMidpoint = 0;
   static int midpointCount = 0;

   public static void Main()
   {
      cte = new CountdownEvent(1);
      // Start three threads. 
      for (int ctr = 0; ctr <= 2; ctr++) {
         cte.AddCount();
         Thread th = new Thread(GenerateNumbers);
         th.Name = "Thread" + ctr.ToString();
         th.Start();
      }
      cte.Signal();
      cte.Wait();
      Console.WriteLine();
      Console.WriteLine("Total midpoint values:  {0,10:N0} ({1:P3})",
                        totalMidpoint, totalMidpoint/((double)totalCount));
      Console.WriteLine("Total number of values: {0,10:N0}", 
                        totalCount);                  
   }

   private static void GenerateNumbers()
   {
      int midpoint = (UPPERBOUND - LOWERBOUND) / 2;
      int value = 0;
      int total = 0;
      int midpt = 0;

      do {
         lock (lockObj) {
            value = rnd.Next(LOWERBOUND, UPPERBOUND);
         }
         if (value == midpoint) { 
            Interlocked.Increment(ref midpointCount);
            midpt++;
         }
         total++;    
      } while (midpointCount < 10000);

      Interlocked.Add(ref totalCount, total);
      Interlocked.Add(ref totalMidpoint, midpt);

      string s = String.Format("Thread {0}:\n", Thread.CurrentThread.Name) +
                 String.Format("   Random Numbers: {0:N0}\n", total) + 
                 String.Format("   Midpoint values: {0:N0} ({1:P3})", midpt, 
                               ((double) midpt)/total);
      Console.WriteLine(s);
      cte.Signal();
   }
}
// The example displays output like the following:
//       Thread Thread2:
//          Random Numbers: 2,776,674
//          Midpoint values: 2,773 (0.100 %)
//       Thread Thread1:
//          Random Numbers: 4,876,100
//          Midpoint values: 4,873 (0.100 %)
//       Thread Thread0:
//          Random Numbers: 2,312,310
//          Midpoint values: 2,354 (0.102 %)
//       
//       Total midpoint values:      10,000 (0.100 %)
//       Total number of values:  9,965,084

निम्नलिखित उदाहरण पिछले एक के समान है, सिवाय इसके कि यह 50,000 यादृच्छिक मिडपॉइंट पूर्णांकों को उत्पन्न करने के लिए थ्रेड प्रक्रिया के बजाय टास्क क्लास का उपयोग करता है। इस उदाहरण में, एक लैम्ब्डा अभिव्यक्ति GenerateNumbers थ्रेड प्रक्रिया को प्रतिस्थापित करता है, और टास्क को कॉल करता है। WitAll विधि काउंटडाउनवेंट ऑब्जेक्ट की आवश्यकता को समाप्त करता है।

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   const int LOWERBOUND = 0;
   const int UPPERBOUND = 1001;

   static Object lockObj = new Object();
   static Random rnd = new Random();

   static int totalCount = 0;
   static int totalMidpoint = 0;
   static int midpointCount = 0;

   public static void Main()
   {
      List<Task> tasks = new List<Task>();
      // Start three tasks. 
      for (int ctr = 0; ctr <= 2; ctr++) 
         tasks.Add(Task.Run( () => { int midpoint = (UPPERBOUND - LOWERBOUND) / 2;
                                     int value = 0;
                                     int total = 0;
                                     int midpt = 0;

                                     do {
                                        lock (lockObj) {
                                           value = rnd.Next(LOWERBOUND, UPPERBOUND);
                                        }
                                        if (value == midpoint) { 
                                           Interlocked.Increment(ref midpointCount);
                                           midpt++;
                                        }
                                        total++;    
                                     } while (midpointCount < 50000);

                                     Interlocked.Add(ref totalCount, total);
                                     Interlocked.Add(ref totalMidpoint, midpt);

                                     string s = String.Format("Task {0}:\n", Task.CurrentId) +
                                                String.Format("   Random Numbers: {0:N0}\n", total) + 
                                                String.Format("   Midpoint values: {0:N0} ({1:P3})", midpt, 
                                                              ((double) midpt)/total);
                                     Console.WriteLine(s); } ));

      Task.WaitAll(tasks.ToArray());
      Console.WriteLine();
      Console.WriteLine("Total midpoint values:  {0,10:N0} ({1:P3})",
                        totalMidpoint, totalMidpoint/((double)totalCount));
      Console.WriteLine("Total number of values: {0,10:N0}", 
                        totalCount);                  
   }
}
// The example displays output like the following:
//       Task 3:
//          Random Numbers: 10,855,250
//          Midpoint values: 10,823 (0.100 %)
//       Task 1:
//          Random Numbers: 15,243,703
//          Midpoint values: 15,110 (0.099 %)
//       Task 2:
//          Random Numbers: 24,107,425
//          Midpoint values: 24,067 (0.100 %)
//       
//       Total midpoint values:      50,000 (0.100 %)
//       Total number of values: 50,206,378

https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.increment?view=netcore-3.0

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.