SpinLock SpinLock SpinLock SpinLock Struct

定義

提供互斥鎖定基本作業,在這個作業中,嘗試取得鎖定的執行緒會用迴圈方式等候,並重複檢查,直到鎖定可用為止。Provides a mutual exclusion lock primitive where a thread trying to acquire the lock waits in a loop repeatedly checking until the lock becomes available.

public value class SpinLock
[System.Runtime.InteropServices.ComVisible(false)]
public struct SpinLock
type SpinLock = struct
Public Structure SpinLock
繼承
屬性

範例

下列範例顯示如何使用SpinLockThe following example shows how to use a 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

備註

如需如何使用微調鎖定的範例,請參閱如何:使用 SpinLock 進行低層級的同步處理。For an example of how to use a Spin Lock, see How to: Use SpinLock for Low-Level Synchronization.

微調鎖定可以用於分Monitor葉層級的鎖定,在這種情況下,使用、大小或垃圾收集壓力所隱含的物件配置會過度耗費資源。Spin locks can be used for leaf-level locks where the object allocation implied by using a Monitor, in size or due to garbage collection pressure, is overly expensive. 微調鎖定可能有助於避免封鎖;不過,如果您預期會有大量的封鎖,您可能不會因為旋轉過多而使用微調鎖定。A spin lock can be useful to avoid blocking; however, if you expect a significant amount of blocking, you should probably not use spin locks due to excessive spinning. 當鎖定是精細且大量的數位(例如,連結清單中每個節點的鎖定),而且鎖定保留時間一律非常短時,旋轉可能會很有説明。Spinning can be beneficial when locks are fine-grained and large in number (for example, a lock per node in a linked list) and also when lock hold-times are always extremely short. 一般來說,在持有微調鎖定的情況下,應避免其中一個動作:In general, while holding a spin lock, one should avoid any of these actions:

  • blocking,

  • 呼叫本身可能封鎖的任何專案,calling anything that itself may block,

  • 一次保留一個以上的微調鎖定,holding more than one spin lock at once,

  • 進行動態分派的呼叫(介面和虛擬函式)、making dynamically dispatched calls (interface and virtuals),

  • 將靜態分派的呼叫設為不屬於任何程式碼,或making statically dispatched calls into any code one doesn't own, or

  • 配置記憶體。allocating memory.

SpinLock只有在您決定這麼做之後,才應該使用它來改善應用程式的效能。SpinLock should only be used after you have been determined that doing so will improve an application's performance. 基於效能考慮,也請務必SpinLock注意,是實值型別。It is also important to note that SpinLock is a value type, for performance reasons. 基於這個理由,您必須非常小心不要不小心複製SpinLock實例,因為這兩個實例(原始和複本)會彼此完全獨立,這可能會導致應用程式發生錯誤的行為。For this reason, you must be very careful not to accidentally copy a SpinLock instance, as the two instances (the original and the copy) would then be completely independent of one another, which would likely lead to erroneous behavior of the application. 如果必須傳入實例,則應該以傳址方式傳遞,而不是透過值。 SpinLockIf a SpinLock instance must be passed around, it should be passed by reference rather than by value.

不要將實例SpinLock儲存在 readonly 欄位中。Do not store SpinLock instances in readonly fields.

建構函式

SpinLock(Boolean) SpinLock(Boolean) SpinLock(Boolean) SpinLock(Boolean)

使用可追蹤執行緒 ID 以改善偵錯的選項,初始化 SpinLock 結構的新執行個體。Initializes a new instance of the SpinLock structure with the option to track thread IDs to improve debugging.

屬性

IsHeld IsHeld IsHeld IsHeld

取得值,這個值表示此鎖定目前是否由任何執行緒持有。Gets whether the lock is currently held by any thread.

IsHeldByCurrentThread IsHeldByCurrentThread IsHeldByCurrentThread IsHeldByCurrentThread

取得值,表示此鎖定是否由目前執行緒持有。Gets whether the lock is held by the current thread.

IsThreadOwnerTrackingEnabled IsThreadOwnerTrackingEnabled IsThreadOwnerTrackingEnabled IsThreadOwnerTrackingEnabled

取得值,表示這個執行個體是否已啟用執行緒擁有權追蹤。Gets whether thread ownership tracking is enabled for this instance.

方法

Enter(Boolean) Enter(Boolean) Enter(Boolean) Enter(Boolean)

以可靠的方式取得鎖定,例如即使方法呼叫中發生例外狀況,還是能可靠地檢查 lockTaken 以判斷是否已取得鎖定。Acquires the lock in a reliable manner, such that even if an exception occurs within the method call, lockTaken can be examined reliably to determine whether the lock was acquired.

Exit() Exit() Exit() Exit()

釋放鎖定。Releases the lock.

Exit(Boolean) Exit(Boolean) Exit(Boolean) Exit(Boolean)

釋放鎖定。Releases the lock.

TryEnter(Boolean) TryEnter(Boolean) TryEnter(Boolean) TryEnter(Boolean)

嘗試以可靠的方式取得鎖定,例如即使方法呼叫中發生例外狀況,還是能可靠地檢查 lockTaken 以判斷是否已取得鎖定。Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within the method call, lockTaken can be examined reliably to determine whether the lock was acquired.

TryEnter(Int32, Boolean) TryEnter(Int32, Boolean) TryEnter(Int32, Boolean) TryEnter(Int32, Boolean)

嘗試以可靠的方式取得鎖定,例如即使方法呼叫中發生例外狀況,還是能可靠地檢查 lockTaken 以判斷是否已取得鎖定。Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within the method call, lockTaken can be examined reliably to determine whether the lock was acquired.

TryEnter(TimeSpan, Boolean) TryEnter(TimeSpan, Boolean) TryEnter(TimeSpan, Boolean) TryEnter(TimeSpan, Boolean)

嘗試以可靠的方式取得鎖定,例如即使方法呼叫中發生例外狀況,還是能可靠地檢查 lockTaken 以判斷是否已取得鎖定。Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within the method call, lockTaken can be examined reliably to determine whether the lock was acquired.

適用於

執行緒安全性

SpinLock所有成員都是安全線程,而且可以同時從多個執行緒使用。All members of SpinLock are thread-safe and may be used from multiple threads concurrently.

另請參閱