Cara: Menggunakan SpinLock untuk Sinkronisasi Tingkat-Rendah
Contoh berikut menunjukkan cara menggunakan suatu SpinLock. Dalam contoh ini, bagian kritis melakukan sejumlah pekerjaan kecil, yang menjadikannya kandidat yang baik untuk SpinLock. Meningkatkan pekerjaan dalam jumlah kecil meningkatkan performa SpinLock dibandingkan dengan kunci standar. Namun, ada titik di mana SpinLock menjadi lebih mahal daripada kunci standar. Anda dapat menggunakan fungsionalitas pembuatan profil konkurensi di alat pembuatan profil untuk melihat jenis kunci mana yang memberikan performa yang lebih baik dalam program Anda. Untuk informasi selengkapnya, lihat Visualizer Konkurensi.
class SpinLockDemo2
{
const int N = 100000;
static Queue<Data> _queue = new Queue<Data>();
static object _lock = new Object();
static SpinLock _spinlock = new SpinLock();
class Data
{
public string Name { get; set; }
public double Number { get; set; }
}
static void Main(string[] args)
{
// First use a standard lock for comparison purposes.
UseLock();
_queue.Clear();
UseSpinLock();
Console.WriteLine("Press a key");
Console.ReadKey();
}
private static void UpdateWithSpinLock(Data d, int i)
{
bool lockTaken = false;
try
{
_spinlock.Enter(ref lockTaken);
_queue.Enqueue( d );
}
finally
{
if (lockTaken) _spinlock.Exit(false);
}
}
private static void UseSpinLock()
{
Stopwatch sw = Stopwatch.StartNew();
Parallel.Invoke(
() => {
for (int i = 0; i < N; i++)
{
UpdateWithSpinLock(new Data() { Name = i.ToString(), Number = i }, i);
}
},
() => {
for (int i = 0; i < N; i++)
{
UpdateWithSpinLock(new Data() { Name = i.ToString(), Number = i }, i);
}
}
);
sw.Stop();
Console.WriteLine("elapsed ms with spinlock: {0}", sw.ElapsedMilliseconds);
}
static void UpdateWithLock(Data d, int i)
{
lock (_lock)
{
_queue.Enqueue(d);
}
}
private static void UseLock()
{
Stopwatch sw = Stopwatch.StartNew();
Parallel.Invoke(
() => {
for (int i = 0; i < N; i++)
{
UpdateWithLock(new Data() { Name = i.ToString(), Number = i }, i);
}
},
() => {
for (int i = 0; i < N; i++)
{
UpdateWithLock(new Data() { Name = i.ToString(), Number = i }, i);
}
}
);
sw.Stop();
Console.WriteLine("elapsed ms with lock: {0}", sw.ElapsedMilliseconds);
}
}
Imports System.Threading
Imports System.Threading.Tasks
Class SpinLockDemo2
Const N As Integer = 100000
Shared _queue = New Queue(Of Data)()
Shared _lock = New Object()
Shared _spinlock = New SpinLock()
Class Data
Public Name As String
Public Number As Double
End Class
Shared Sub Main()
' First use a standard lock for comparison purposes.
UseLock()
_queue.Clear()
UseSpinLock()
Console.WriteLine("Press a key")
Console.ReadKey()
End Sub
Private Shared Sub UpdateWithSpinLock(ByVal d As Data, ByVal i As Integer)
Dim lockTaken As Boolean = False
Try
_spinlock.Enter(lockTaken)
_queue.Enqueue(d)
Finally
If lockTaken Then
_spinlock.Exit(False)
End If
End Try
End Sub
Private Shared Sub UseSpinLock()
Dim sw = Stopwatch.StartNew()
Parallel.Invoke(
Sub()
For i As Integer = 0 To N - 1
UpdateWithSpinLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
Next
End Sub,
Sub()
For i As Integer = 0 To N - 1
UpdateWithSpinLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
Next
End Sub
)
sw.Stop()
Console.WriteLine("elapsed ms with spinlock: {0}", sw.ElapsedMilliseconds)
End Sub
Shared Sub UpdateWithLock(ByVal d As Data, ByVal i As Integer)
SyncLock (_lock)
_queue.Enqueue(d)
End SyncLock
End Sub
Private Shared Sub UseLock()
Dim sw = Stopwatch.StartNew()
Parallel.Invoke(
Sub()
For i As Integer = 0 To N - 1
UpdateWithLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
Next
End Sub,
Sub()
For i As Integer = 0 To N - 1
UpdateWithLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
Next
End Sub
)
sw.Stop()
Console.WriteLine("elapsed ms with lock: {0}", sw.ElapsedMilliseconds)
End Sub
End Class
SpinLock mungkin berguna ketika kunci pada sumber daya bersama tidak akan ditahan untuk waktu yang sangat lama. Dalam kasus seperti itu, pada komputer multi-inti dapat menjadi efisien bagi utas yang diblokir untuk berputar untuk sedikit putaran sampai kunci terlepas. Dengan memutar, utas tidak menjadi diblokir, yang menjadi proses intensif-CPU. SpinLock akan berhenti berputar dalam kondisi tertentu untuk mencegah kekurangan prosesor logis atau inversi prioritas pada sistem dengan Hyper-Threading.
Contoh ini menggunakan kelas System.Collections.Generic.Queue<T>, yang memerlukan sinkronisasi pengguna untuk akses multi-utas. Opsi lain adalah menggunakan System.Collections.Concurrent.ConcurrentQueue<T>, yang tidak memerlukan kunci pengguna apa pun.
Perhatikan penggunaan false
dalam panggilan ke SpinLock.Exit. Ini memberikan performa terbaik. Tentukan true
pada arsitektur IA64 untuk menggunakan pagar memori, yang membersihkan buffer tulis untuk memastikan bahwa kunci sekarang tersedia untuk dimasuki utas lain.
Lihat juga
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk