Csharp/CSharp Tutorial/Thread/Custom Lock

Материал из .Net Framework эксперт
Перейти к: навигация, поиск

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