2016-05-10 20 views
0

Nur aus Neugier habe ich ein Programm gemacht, um die Leistung von InterLocked vs Lock in .Net zu testen. Es stellt sich heraus, Interlocked Version ist viel langsamer als Locked-Version, kann jemand bitte darauf hinweisen, wenn ich hier einige Details vermisse. Nach meinem Verständnis sollte Interlocked viel besser als Sperre durchführen.Warum ist Interlocked langsamer als Sperre?

public class TestB 
    { 
     private static readonly object _objLocker = new object(); 
     private long _shared; 
     public void IncrLocked() 
     { 
      lock (_objLocker) 
      { 
       _shared++; 
      } 
     } 
     public void IncrInterLocked() 
     { 
      Interlocked.Increment(ref _shared); 
     } 
     public long GetValue() 
     { 
      return _shared; 
     } 
    } 
    class TestsCopy 
    { 
     private static TestB _testB = new TestB(); 
     static void Main(string[] args) 
     { 
      int numofthreads = 100; 
      TestInterLocked(numofthreads); 
      TestLocked(numofthreads); 
      Console.ReadLine(); 
     } 
     private static void TestInterLocked(int numofthreads) 
     { 
      Thread[] threads = new Thread[numofthreads]; 
      for (int i = 0; i < numofthreads; i++) 
      { 
       Thread t = new Thread(StartTestInterLocked); 
       threads[i] = t; 
       t.Start(); 
      } 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 
      for (int i = 0; i < threads.Length; i++) 
      { 
       threads[i].Join(); 
      } 
      sw.Stop(); 
      Console.WriteLine($"Interlocked finished in : {sw.ElapsedMilliseconds}, value = {_testB.GetValue()}"); 
     } 

     private static void TestLocked(int numofthreads) 
     { 
      Thread[] threads = new Thread[numofthreads]; 
      for (int i = 0; i < numofthreads; i++) 
      { 
       Thread t = new Thread(StartTestLocked); 
       threads[i] = t; 
       t.Start(); 
      } 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 
      for (int i = 0; i < threads.Length; i++) 
      { 
       threads[i].Join(); 
      } 
      sw.Stop(); 
      Console.WriteLine($"Locked finished in : {sw.ElapsedMilliseconds}, value = {_testB.GetValue()}"); 
     } 

     private static void StartTestInterLocked() 
     { 
      int counter = 10000000; 
      for (int i = 0; i < counter; i++) 
      { 
       _testB.IncrInterLocked(); 
      } 
     } 
     private static void StartTestLocked() 
     { 
      int counter = 10000000; 
      for (int i = 0; i < counter; i++) 
      { 
       _testB.IncrLocked(); 
      } 
     } 

Die Ausgabe des Programms ist ...

Interlocked finished in : 76909 ms, value = 1000000000 
Locked finished in : 44215 ms, value = 2000000000 
+0

Sie testen nur den Fall, in dem es hier extrem viele gleichzeitige Zugriffe gibt. Für einen faireren Test sollten Sie auch testen, was schneller ist, wenn kein gleichzeitiger Zugriff oder ein normaler Wert vorhanden ist. – hvd

+4

Ich denke, Ihre Messung ist fehlerhaft. Sie starten alle Threads, bevor Sie die Stoppuhr starten. Viele, wenn nicht die meisten der Threads werden abgeschlossen sein, bevor die Messung beginnt. –

Antwort

3

Ihr Test ist aus dem Grund, erwähnt von Jakob Olsen fehlerhaft. Darüber hinaus enthält Ihr Test auch den Overhead von Aufrufmethoden in einer Klasse (für die lock, offensichtlich kann das nicht vermieden werden für den Aufruf .

Sie sollten alle Threads starten und veranlassen, dass sie anfangen, nach Ihnen zu arbeiten habe die Stoppuhr gestartet Sie tun können, dass sie auf einem ManualResetEvent warten, indem sie

ich habe wie so Ihre Test-Code neu geschrieben:..

using System; 
using System.Diagnostics; 
using System.Threading; 

namespace Demo 
{ 
    static class Program 
    { 
     private static readonly object _objLocker = new object(); 
     private static long _shared; 
     private static ManualResetEvent _signal = new ManualResetEvent(false); 

     static void Main(string[] args) 
     { 
      int numofthreads = 100; 
      TestInterLocked(numofthreads); 
      TestLocked(numofthreads); 
      Console.ReadLine(); 
     } 

     private static void TestInterLocked(int numofthreads) 
     { 
      Thread[] threads = new Thread[numofthreads]; 
      for (int i = 0; i < numofthreads; i++) 
      { 
       Thread t = new Thread(StartTestInterLocked); 
       threads[i] = t; 
       t.Start(); 
      } 
      Thread.Sleep(5000); // Make sure threads have had time to start. 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 
      _shared = 0; 
      _signal.Set(); 

      for (int i = 0; i < threads.Length; i++) 
      { 
       threads[i].Join(); 
      } 

      sw.Stop(); 
      _signal.Reset(); 
      Console.WriteLine($"Interlocked finished in : {sw.ElapsedMilliseconds}, value = {_shared}"); 
     } 

     private static void TestLocked(int numofthreads) 
     { 
      Thread[] threads = new Thread[numofthreads]; 
      for (int i = 0; i < numofthreads; i++) 
      { 
       Thread t = new Thread(StartTestLocked); 
       threads[i] = t; 
       t.Start(); 
      } 
      Thread.Sleep(5000); // Make sure threads have had time to start. 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 
      _shared = 0; 
      _signal.Set(); 

      for (int i = 0; i < threads.Length; i++) 
      { 
       threads[i].Join(); 
      } 

      sw.Stop(); 
      _signal.Reset(); 
      Console.WriteLine($"Locked finished in : {sw.ElapsedMilliseconds}, value = {_shared}"); 
     } 

     private static void StartTestInterLocked() 
     { 
      _signal.WaitOne(); 
      int counter = 10000000; 
      for (int i = 0; i < counter; i++) 
      { 
       Interlocked.Increment(ref _shared); 
      } 
     } 

     private static void StartTestLocked() 
     { 
      _signal.WaitOne(); 
      int counter = 10000000; 
      for (int i = 0; i < counter; i++) 
      { 
       lock (_objLocker) 
       { 
        _shared++; 
       } 
      } 
     } 
    } 
} 

Nun sind die Ergebnisse (von einem Release-Build):

Interlocked finished in : 11339, value = 1000000000 
Locked finished in : 30546, value = 1000000000 

Wie Sie sehen können, ist Interlocked wesentlich schneller.

+0

auf meiner Maschine ist die Ausgabe gesperrt In: 139614, Wert = 1000000000 Gesperrt fertig in: 30432, Wert = 1000000000 –

+0

Ich vermisse einige Optimierung, warum habe ich auf meinem Rechner so langsam Interlocked? –

+0

@Bovi_Khurja Sie führen definitiv eine Release-Version außerhalb des Debuggers aus? Und haben Sie ein 32-Bit- oder ein 64-Bit-Betriebssystem? Erstellen Sie die ausführbare Datei als x64/AnyCPU? –

Verwandte Themen