Відповіді:
Як рекомендують інші, Interlocked.Increment
воля матиме кращі показники, ніж lock()
. Просто подивіться на ІР та збори, де ви це побачитеIncrement
перетворюється на заяву "блокування шини", а його змінна безпосередньо збільшується (x86) або "додається" до (x64).
Це твердження "блокування шини" блокує шину, щоб запобігти доступу іншого шини до процесора, поки викликає процесор виконує свою роботу. А тепер подивіться на lock()
ІР заявки C # . Тут ви побачите дзвінки Monitor
, щоб розпочати або закінчити розділ.
Іншими словами, .Net lock()
операція робить набагато більше, ніж .Net Interlocked.Increment
.
Так, якщо все, що ви хочете зробити, це збільшення змінної, Interlock.Increment
буде швидше. Перегляньте всі перемикані методи, щоб побачити різні доступні атомні операції та знайти ті, що відповідають вашим потребам. Використовуйте, lock()
коли ви хочете робити більш складні речі, такі як кілька взаємопов'язаних приростів / декрементів, або серіалізувати доступ до ресурсів, складніших за цілі числа.
Я пропоную вам використовувати .NET вбудований з кроком блокування в бібліотеці System.Threading.
Наступний код збільшує довгу змінну за посиланням і повністю безпечний для потоків:
Interlocked.Increment(ref myNum);
Джерело: http://msdn.microsoft.com/en-us/library/dd78zt0c.aspx
Спробуйте з Interlocked.Increment
Як вже згадувалося використання Interlocked.Increment
Приклад коду з MS:
Наступний приклад визначає, скільки випадкових чисел у межах від 0 до 1000 потрібно для генерування 1000 випадкових чисел із середнім значенням. Щоб відслідковувати кількість значень середньої точки, змінна, midpointCount встановлюється рівною 0 і збільшується щоразу, коли генератор випадкових чисел повертає середнє значення, поки воно не досягне 10000. Оскільки три потоки генерують випадкові числа, метод Increment (Int32) викликається, щоб переконатися, що кілька потоків не оновлюють midpointCount одночасно. Зауважте, що блокування також використовується для захисту генератора випадкових чисел і що об'єкт CountdownEvent використовується для того, щоб метод Main не закінчив виконання до трьох потоків.
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
Наступний приклад схожий на попередній, за винятком того, що він використовує клас Task замість потокової процедури для генерування 50 000 випадкових цілих чисел середньої точки. У цьому прикладі лямбда-вираз замінює процедуру потоку GenerateNumbers, а виклик методу Task.WaitAll усуває потребу в об'єкті CountdownEvent.
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