Yanıtlar:
Başkaları tarafından önerildiği gibi, Interlocked.Increment
performans daha iyi olacaktır lock()
. Sadece Increment
"bus lock" deyimine dönüştüğünü ve değişkeninin doğrudan (x86) veya (x64) 'e "eklendiğini" göreceğiniz IL ve Assembly'ye bir göz atın .
Bu "bus lock" ifadesi, çağrılan CPU çalışırken başka bir CPU'nun veri yoluna erişmesini önlemek için veri yolunu kilitler. Şimdi, C # lock()
ifadesinin IL'sine bir göz atın . Burada Monitor
bir bölümü başlatmak veya bitirmek için yapılan çağrıları göreceksiniz .
Başka bir deyişle, .Net lock()
deyimi .Net'ten çok daha fazlasını yapıyor Interlocked.Increment
.
Yani, tüm yapmak istediğiniz bir değişkeni artırmak ise, Interlock.Increment
daha hızlı olacaktır. Mevcut çeşitli atomik işlemleri görmek ve ihtiyaçlarınıza uygun olanları bulmak için tüm Kilitli yöntemleri gözden geçirin. lock()
Birden fazla birbiriyle ilişkili artış / azalma gibi daha karmaşık şeyler yapmak veya tamsayılardan daha karmaşık olan kaynaklara erişimi serileştirmek istediğinizde kullanın .
System.Threading kitaplığında .NET'in yerleşik kilitleme artışını kullanmanızı öneririm.
Aşağıdaki kod uzun bir değişkeni referans olarak artırır ve tamamen iş parçacığı için güvenlidir:
Interlocked.Increment(ref myNum);
Kaynak: http://msdn.microsoft.com/en-us/library/dd78zt0c.aspx
Interlocked ile deneyin.
Daha önce de belirtildiği gibi kullanım Interlocked.Increment
MS kod örneği:
Aşağıdaki örnek, bir orta nokta değerine sahip 1.000 rasgele sayı oluşturmak için 0 ile 1.000 arasında kaç rasgele sayının gerektiğini belirler. Orta nokta değerlerinin sayısını izlemek için, bir değişken, midpointCount, 0'a eşit olarak ayarlanır ve rastgele sayı üretecinin 10.000'e ulaşana kadar bir orta nokta değeri döndürdüğü her seferde artırılır. Üç iş parçacığı rasgele sayılar ürettiğinden, birden çok iş parçacığının midpointCount'u eşzamanlı olarak güncellemediğinden emin olmak için Increment (Int32) yöntemi çağrılır. Rastgele sayı üretecini korumak için bir kilit de kullanıldığını ve Main yönteminin üç iş parçacığından önce yürütmeyi bitirmediğinden emin olmak için bir CountdownEvent nesnesi kullanıldığını unutmayın.
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
Aşağıdaki örnek, öncekine benzer, ancak 50.000 rasgele orta nokta tamsayıları oluşturmak için bir iş parçacığı yordamı yerine Görev sınıfını kullanmasıdır. Bu örnekte, lambda ifadesi GenerateNumbers iş parçacığı yordamının yerini alır ve Task.WaitAll yöntemine yapılan çağrı CountdownEvent nesnesine olan gereksinimi ortadan kaldırır.
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