Nasıl yapılır: Düşük düzeyli eşitleme için SpinLock kullanma
Aşağıdaki örnekte nasıl kullanılacağı gösterilmektedir SpinLock. Bu örnekte, kritik bölüm en az miktarda çalışma gerçekleştirir ve bu da onu için iyi bir SpinLockaday yapar. İşin küçük bir miktar artırılması, standart kilit ile SpinLock karşılaştırıldığında performansını artırır. Ancak, SpinLock'un standart kilitten daha pahalı olduğu bir nokta vardır. Hangi kilit türünün programınızda daha iyi performans sağladığını görmek için profil oluşturma araçlarında eşzamanlılık profili oluşturma işlevini kullanabilirsiniz. Daha fazla bilgi için bkz . Eşzamanlılık Görselleştiricisi.
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 paylaşılan bir kaynak üzerindeki kilit çok uzun süre tutulmayacaksa yararlı olabilir. Böyle durumlarda, çok çekirdekli bilgisayarlarda kilit serbest bırakılana kadar engellenen iş parçacığının birkaç döngü boyunca dönmesi verimli olabilir. Dönen iş parçacığı engellenmez ve bu yoğun CPU kullanan bir işlemdir. SpinLock , Hyper-Threading'e sahip sistemlerde mantıksal işlemcilerin yetersizliğini veya öncelik ters çevirmeyi önlemek için belirli koşullar altında döndürmeyi durdurur.
Bu örnek, çok iş parçacıklı erişim için kullanıcı eşitlemesi gerektiren sınıfını kullanır System.Collections.Generic.Queue<T> . Başka bir seçenek, System.Collections.Concurrent.ConcurrentQueue<T>kullanıcı kilidi gerektirmeyen kullanmaktır.
çağrısında kullanımına false
dikkat edin SpinLock.Exit. Bu, en iyi performansı sağlar. Bellek çitini kullanmak için IA64 mimarilerinde belirtin true
. Bu, kilidin artık diğer iş parçacıklarının girebilmesi için yazma arabelleklerini temizler.