Câu trả lời:
Theo khuyến cáo của người khác, Interlocked.Increment
sẽ có hiệu suất tốt hơn lock()
. Chỉ cần nhìn vào IL và hội nơi bạn sẽ thấy Increment
biến thành câu lệnh "khóa xe buýt" và biến của nó được tăng trực tiếp (x86) hoặc "thêm" vào (x64).
Câu lệnh "khóa xe buýt" này khóa xe buýt để ngăn CPU khác truy cập vào xe buýt trong khi CPU gọi thực hiện hoạt động. Bây giờ, hãy xem lock()
IL của tuyên bố C # . Tại đây bạn sẽ thấy các cuộc gọi đến Monitor
để bắt đầu hoặc kết thúc một phần.
Nói cách khác, lock()
câu lệnh .Net đang làm nhiều hơn so với .Net Interlocked.Increment
.
Vì vậy, nếu tất cả những gì bạn muốn làm là tăng một biến, Interlock.Increment
sẽ nhanh hơn. Xem lại tất cả các phương pháp lồng vào nhau để xem các hoạt động nguyên tử khác nhau có sẵn và để tìm ra các phương pháp phù hợp với nhu cầu của bạn. Sử dụng lock()
khi bạn muốn thực hiện những việc phức tạp hơn như tăng / giảm liên quan đến nhau hoặc để tuần tự hóa quyền truy cập vào các tài nguyên phức tạp hơn số nguyên.
Tôi đề nghị bạn sử dụng gia tăng liên khóa tích hợp của .NET trong thư viện System.Threading.
Đoạn mã sau sẽ tăng một biến dài theo tham chiếu và hoàn toàn an toàn cho chuỗi:
Interlocked.Increment(ref myNum);
Nguồn: http://msdn.microsoft.com/en-us/l Library / dd78zt0c.aspx
Hãy thử với Interlocked.Increment
Như đã đề cập sử dụng Interlocked.Increment
Mã ví dụ từ MS:
Ví dụ sau đây xác định có bao nhiêu số ngẫu nhiên nằm trong khoảng từ 0 đến 1.000 được yêu cầu để tạo 1.000 số ngẫu nhiên có giá trị trung điểm. Để theo dõi số lượng giá trị trung điểm, một biến, midpointCount, được đặt bằng 0 và tăng lên mỗi khi trình tạo số ngẫu nhiên trả về giá trị trung điểm cho đến khi đạt 10.000. Vì ba luồng tạo ra các số ngẫu nhiên, phương thức Tăng (Int32) được gọi để đảm bảo rằng nhiều luồng không cập nhật đồng thời midpointCount. Lưu ý rằng khóa cũng được sử dụng để bảo vệ trình tạo số ngẫu nhiên và đối tượng CountdownEvent được sử dụng để đảm bảo rằng phương thức Main không hoàn thành thực thi trước ba luồng.
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
Ví dụ sau tương tự như ví dụ trước, ngoại trừ việc nó sử dụng lớp Nhiệm vụ thay vì thủ tục luồng để tạo 50.000 số nguyên trung điểm ngẫu nhiên. Trong ví dụ này, một biểu thức lambda thay thế thủ tục luồng GenerateNumbers và lệnh gọi phương thức Task.WaitAll loại bỏ sự cần thiết của đối tượng 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