(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
A spin-lock
/*
Quote from
Professional .NET Framework 2.0 (Programmer to Programmer) (Paperback)
by Joe Duffy (Author)
# Paperback: 601 pages
# Publisher: Wrox (April 10, 2006)
# Language: English
# ISBN-10: 0764571354
# ISBN-13: 978-0764571350
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime;
using System.Runtime.rupilerServices;
using System.Security;
using System.Text;
using System.Threading;
class SpinLock
{
private int state;
private EventWaitHandle available = new AutoResetEvent(false);
// This looks at the total number of hardware threads available; if it"s
// only 1, we will use an optimized code path
private static bool isSingleProc = (Environment.ProcessorCount == 1);
private const int outerTryCount = 5;
private const int cexTryCount = 100;
public void Enter(out bool taken)
{
// Taken is an out parameter so that we set it *inside* the critical
// region, rather than returning it and permitting aborts to creep in.
// Without this, the caller could take the lock, but not release it
// because it didn"t know it had to.
taken = false;
while (!taken)
{
if (isSingleProc)
{
// Don"t busy wait on 1-logical processor machines; try
// a single swap, and if it fails, drop back to EventWaitHandle.
Thread.BeginCriticalRegion();
taken = Interlocked.rupareExchange(ref state, 1, 0) == 0;
if (!taken)
Thread.EndCriticalRegion();
}
else
{
for (int i = 0; !taken && i < outerTryCount; i++)
{
// Tell the CLR we"re in a critical region;
// interrupting could lead to deadlocks.
Thread.BeginCriticalRegion();
// Try "cexTryCount" times to CEX the state variable:
int tries = 0;
while (!(taken =
Interlocked.rupareExchange(ref state, 1, 0) == 0) &&
tries++ < cexTryCount)
{
Thread.SpinWait(1);
}
if (!taken)
{
// We failed to acquire in the busy spin, mark the end
// of our critical region and yield to let another
// thread make forward progress.
Thread.EndCriticalRegion();
Thread.Sleep(0);
}
}
}
// If we didn"t acquire the lock, block.
if (!taken) available.WaitOne();
}
return;
}
public void Enter()
{
// Convenience method. Using this could be prone to deadlocks.
bool b;
Enter(out b);
}
public void Exit()
{
if (Interlocked.rupareExchange(ref state, 0, 1) == 1)
{
// We notify the waking threads inside our critical region so
// that an abort doesn"t cause us to lose a pulse, (which could
// lead to deadlocks).
available.Set();
Thread.EndCriticalRegion();
}
}
}
public class MainClass
{
public static void Main()
{
SpinLock sl1 = new SpinLock();
sl1.Enter();
try
{
Console.WriteLine("Acquired the spin lock");
}
finally
{
sl1.Exit();
Console.WriteLine("Released the spin lock");
}
}
}
Acquired the spin lock
Released the spin lock