SpinLock 结构 
定义
重要
一些信息与预发行产品相关,相应产品在发行之前可能会进行重大修改。 对于此处提供的信息,Microsoft 不作任何明示或暗示的担保。
提供一个相互排斥锁基元,在该基元中,尝试获取锁的线程将在重复检查的循环中等待,直至该锁变为可用为止。
public value class SpinLockpublic struct SpinLock[System.Runtime.InteropServices.ComVisible(false)]
public struct SpinLocktype SpinLock = struct[<System.Runtime.InteropServices.ComVisible(false)>]
type SpinLock = structPublic Structure SpinLock- 继承
- 属性
示例
以下示例演示如何使用 SpinLock:
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class SpinLockDemo
{
    // Demonstrates:
    //      Default SpinLock construction ()
    //      SpinLock.Enter(ref bool)
    //      SpinLock.Exit()
    static void SpinLockSample1()
    {
        SpinLock sl = new SpinLock();
        StringBuilder sb = new StringBuilder();
        // Action taken by each parallel job.
        // Append to the StringBuilder 10000 times, protecting
        // access to sb with a SpinLock.
        Action action = () =>
        {
            bool gotLock = false;
            for (int i = 0; i < 10000; i++)
            {
                gotLock = false;
                try
                {
                    sl.Enter(ref gotLock);
                    sb.Append((i % 10).ToString());
                }
                finally
                {
                    // Only give up the lock if you actually acquired it
                    if (gotLock) sl.Exit();
                }
            }
        };
        // Invoke 3 concurrent instances of the action above
        Parallel.Invoke(action, action, action);
        // Check/Show the results
        Console.WriteLine("sb.Length = {0} (should be 30000)", sb.Length);
        Console.WriteLine("number of occurrences of '5' in sb: {0} (should be 3000)",
            sb.ToString().Where(c => (c == '5')).Count());
    }
    // Demonstrates:
    //      Default SpinLock constructor (tracking thread owner)
    //      SpinLock.Enter(ref bool)
    //      SpinLock.Exit() throwing exception
    //      SpinLock.IsHeld
    //      SpinLock.IsHeldByCurrentThread
    //      SpinLock.IsThreadOwnerTrackingEnabled
    static void SpinLockSample2()
    {
        // Instantiate a SpinLock
        SpinLock sl = new SpinLock();
        // These MRESs help to sequence the two jobs below
        ManualResetEventSlim mre1 = new ManualResetEventSlim(false);
        ManualResetEventSlim mre2 = new ManualResetEventSlim(false);
        bool lockTaken = false;
        Task taskA = Task.Factory.StartNew(() =>
        {
            try
            {
                sl.Enter(ref lockTaken);
                Console.WriteLine("Task A: entered SpinLock");
                mre1.Set(); // Signal Task B to commence with its logic
                // Wait for Task B to complete its logic
                // (Normally, you would not want to perform such a potentially
                // heavyweight operation while holding a SpinLock, but we do it
                // here to more effectively show off SpinLock properties in
                // taskB.)
                mre2.Wait();
            }
            finally
            {
                if (lockTaken) sl.Exit();
            }
        });
        Task taskB = Task.Factory.StartNew(() =>
        {
            mre1.Wait(); // wait for Task A to signal me
            Console.WriteLine("Task B: sl.IsHeld = {0} (should be true)", sl.IsHeld);
            Console.WriteLine("Task B: sl.IsHeldByCurrentThread = {0} (should be false)", sl.IsHeldByCurrentThread);
            Console.WriteLine("Task B: sl.IsThreadOwnerTrackingEnabled = {0} (should be true)", sl.IsThreadOwnerTrackingEnabled);
            try
            {
                sl.Exit();
                Console.WriteLine("Task B: Released sl, should not have been able to!");
            }
            catch (Exception e)
            {
                Console.WriteLine("Task B: sl.Exit resulted in exception, as expected: {0}", e.Message);
            }
            mre2.Set(); // Signal Task A to exit the SpinLock
        });
        // Wait for task completion and clean up
        Task.WaitAll(taskA, taskB);
        mre1.Dispose();
        mre2.Dispose();
    }
    // Demonstrates:
    //      SpinLock constructor(false) -- thread ownership not tracked
    static void SpinLockSample3()
    {
        // Create SpinLock that does not track ownership/threadIDs
        SpinLock sl = new SpinLock(false);
        // Used to synchronize with the Task below
        ManualResetEventSlim mres = new ManualResetEventSlim(false);
        // We will verify that the Task below runs on a separate thread
        Console.WriteLine("main thread id = {0}", Thread.CurrentThread.ManagedThreadId);
        // Now enter the SpinLock.  Ordinarily, you would not want to spend so
        // much time holding a SpinLock, but we do it here for the purpose of 
        // demonstrating that a non-ownership-tracking SpinLock can be exited 
        // by a different thread than that which was used to enter it.
        bool lockTaken = false;
        sl.Enter(ref lockTaken);
        // Create a separate Task from which to Exit() the SpinLock
        Task worker = Task.Factory.StartNew(() =>
        {
            Console.WriteLine("worker task thread id = {0} (should be different than main thread id)",
                Thread.CurrentThread.ManagedThreadId);
            // Now exit the SpinLock
            try
            {
                sl.Exit();
                Console.WriteLine("worker task: successfully exited SpinLock, as expected");
            }
            catch (Exception e)
            {
                Console.WriteLine("worker task: unexpected failure in exiting SpinLock: {0}", e.Message);
            }
            // Notify main thread to continue
            mres.Set();
        });
        // Do this instead of worker.Wait(), because worker.Wait() could inline the worker Task,
        // causing it to be run on the same thread.  The purpose of this example is to show that
        // a different thread can exit the SpinLock created (without thread tracking) on your thread.
        mres.Wait();
        // now Wait() on worker and clean up
        worker.Wait();
        mres.Dispose();
    }
}
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks
Module SpinLockDemo
    ' Demonstrates:
    ' Default SpinLock construction ()
    ' SpinLock.Enter(ref bool)
    ' SpinLock.Exit()
    Private Sub SpinLockSample1()
        Dim sl As New SpinLock()
        Dim sb As New StringBuilder()
        ' Action taken by each parallel job.
        ' Append to the StringBuilder 10000 times, protecting
        ' access to sb with a SpinLock.
        Dim action As Action =
            Sub()
                Dim gotLock As Boolean = False
                For i As Integer = 0 To 9999
                    gotLock = False
                    Try
                        sl.Enter(gotLock)
                        sb.Append((i Mod 10).ToString())
                    Finally
                        ' Only give up the lock if you actually acquired it
                        If gotLock Then
                            sl.[Exit]()
                        End If
                    End Try
                Next
            End Sub
        ' Invoke 3 concurrent instances of the action above
        Parallel.Invoke(action, action, action)
        ' Check/Show the results
        Console.WriteLine("sb.Length = {0} (should be 30000)", sb.Length)
        Console.WriteLine("number of occurrences of '5' in sb: {0} (should be 3000)", sb.ToString().Where(Function(c) (c = "5"c)).Count())
    End Sub
    ' Demonstrates:
    ' Default SpinLock constructor (tracking thread owner)
    ' SpinLock.Enter(ref bool)
    ' SpinLock.Exit() throwing exception
    ' SpinLock.IsHeld
    ' SpinLock.IsHeldByCurrentThread
    ' SpinLock.IsThreadOwnerTrackingEnabled
    Private Sub SpinLockSample2()
        ' Instantiate a SpinLock
        Dim sl As New SpinLock()
        ' These MRESs help to sequence the two jobs below
        Dim mre1 As New ManualResetEventSlim(False)
        Dim mre2 As New ManualResetEventSlim(False)
        Dim lockTaken As Boolean = False
        Dim taskA As Task = Task.Factory.StartNew(
            Sub()
                Try
                    sl.Enter(lockTaken)
                    Console.WriteLine("Task A: entered SpinLock")
                    mre1.[Set]()
                    ' Signal Task B to commence with its logic
                    ' Wait for Task B to complete its logic
                    ' (Normally, you would not want to perform such a potentially
                    ' heavyweight operation while holding a SpinLock, but we do it
                    ' here to more effectively show off SpinLock properties in
                    ' taskB.)
                    mre2.Wait()
                Finally
                    If lockTaken Then
                        sl.[Exit]()
                    End If
                End Try
            End Sub)
        Dim taskB As Task = Task.Factory.StartNew(
            Sub()
                mre1.Wait()
                ' wait for Task A to signal me
                Console.WriteLine("Task B: sl.IsHeld = {0} (should be true)", sl.IsHeld)
                Console.WriteLine("Task B: sl.IsHeldByCurrentThread = {0} (should be false)", sl.IsHeldByCurrentThread)
                Console.WriteLine("Task B: sl.IsThreadOwnerTrackingEnabled = {0} (should be true)", sl.IsThreadOwnerTrackingEnabled)
                Try
                    sl.[Exit]()
                    Console.WriteLine("Task B: Released sl, should not have been able to!")
                Catch e As Exception
                    Console.WriteLine("Task B: sl.Exit resulted in exception, as expected: {0}", e.Message)
                End Try
                ' Signal Task A to exit the SpinLock
                mre2.[Set]()
            End Sub)
        ' Wait for task completion and clean up
        Task.WaitAll(taskA, taskB)
        mre1.Dispose()
        mre2.Dispose()
    End Sub
    ' Demonstrates:
    ' SpinLock constructor(false) -- thread ownership not tracked
    Private Sub SpinLockSample3()
        ' Create SpinLock that does not track ownership/threadIDs
        Dim sl As New SpinLock(False)
        ' Used to synchronize with the Task below
        Dim mres As New ManualResetEventSlim(False)
        ' We will verify that the Task below runs on a separate thread
        Console.WriteLine("main thread id = {0}", Thread.CurrentThread.ManagedThreadId)
        ' Now enter the SpinLock.  Ordinarily, you would not want to spend so
        ' much time holding a SpinLock, but we do it here for the purpose of 
        ' demonstrating that a non-ownership-tracking SpinLock can be exited 
        ' by a different thread than that which was used to enter it.
        Dim lockTaken As Boolean = False
        sl.Enter(lockTaken)
        ' Create a separate Task
        Dim worker As Task = Task.Factory.StartNew(
            Sub()
                Console.WriteLine("worker task thread id = {0} (should be different than main thread id)", Thread.CurrentThread.ManagedThreadId)
                ' Now exit the SpinLock
                Try
                    sl.[Exit]()
                    Console.WriteLine("worker task: successfully exited SpinLock, as expected")
                Catch e As Exception
                    Console.WriteLine("worker task: unexpected failure in exiting SpinLock: {0}", e.Message)
                End Try
                ' Notify main thread to continue
                mres.[Set]()
            End Sub)
        ' Do this instead of worker.Wait(), because worker.Wait() could inline the worker Task,
        ' causing it to be run on the same thread. The purpose of this example is to show that
        ' a different thread can exit the SpinLock created (without thread tracking) on your thread.
        mres.Wait()
        ' now Wait() on worker and clean up
        worker.Wait()
        mres.Dispose()
    End Sub
End Module
注解
有关如何使用 Spin Lock 的示例,请参阅 如何:使用 SpinLock 进行Low-Level同步。
旋转锁可用于叶级锁,其中使用大小或垃圾回收压力隐含 Monitor的对象分配过于昂贵。 旋转锁可用于避免阻塞;但是,如果你期望大量的阻塞,你可能不应该使用旋转锁,因为旋转过多。 当锁细粒度和大量 (时,旋转可能很有用,例如,链接列表中的每个节点的锁) ,并且当锁保持时间始终非常短时。 一般情况下,在按住旋转锁时,应避免以下任何操作:
- 阻塞 
- 调用本身可能阻止的任何内容, 
- 同时按住多个旋转锁, 
- (接口和虚拟) 进行动态调度调用, 
- 对任何代码不拥有或 
- 分配内存。 
SpinLock 只有在确定这样做可以提高应用程序性能后才应使用。 另请务必注意, SpinLock 这是一种值类型,出于性能原因。 因此,必须非常小心,不要意外复制实例,因为两个 SpinLock 实例 (原始实例,然后副本) 将完全独立于另一个实例,这可能会导致应用程序的错误行为。 SpinLock如果必须传递实例,则应通过引用而不是按值传递它。
不要将实例存储在 SpinLock 只读字段中。
构造函数
| SpinLock(Boolean) | 使用用于跟踪线程 ID 以改善调试的选项初始化 SpinLock 结构的新实例。 | 
属性
| IsHeld | 获取锁当前是否已由任何线程占用。 | 
| IsHeldByCurrentThread | 获取锁是否已由当前线程占用。 | 
| IsThreadOwnerTrackingEnabled | 获取是否已为此实例启用了线程所有权跟踪。 | 
方法
| Enter(Boolean) | 采用可靠的方式获取锁,这样,即使在方法调用中发生异常的情况下,都能采用可靠的方式检查  | 
| Exit() | 释放锁。 | 
| Exit(Boolean) | 释放锁。 | 
| TryEnter(Boolean) | 尝试采用可靠的方式获取锁,这样,即使在方法调用中发生异常的情况下,都能采用可靠的方式检查   | 
| TryEnter(Int32, Boolean) | 尝试采用可靠的方式获取锁,这样,即使在方法调用中发生异常的情况下,都能采用可靠的方式检查   | 
| TryEnter(TimeSpan, Boolean) | 尝试采用可靠的方式获取锁,这样,即使在方法调用中发生异常的情况下,都能采用可靠的方式检查   | 
适用于
线程安全性
所有成员 SpinLock 都是线程安全的,可以同时从多个线程使用。