SpinLock 结构
定义
重要
一些信息与预发行产品相关,相应产品在发行之前可能会进行重大修改。 对于此处提供的信息,Microsoft 不作任何明示或暗示的担保。
提供一个相互排斥锁基元,在该基元中,尝试获取锁的线程将在重复检查的循环中等待,直至该锁变为可用为止。
public value class SpinLock
public struct SpinLock
[System.Runtime.InteropServices.ComVisible(false)]
public struct SpinLock
type SpinLock = struct
[<System.Runtime.InteropServices.ComVisible(false)>]
type SpinLock = struct
Public 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 都是线程安全的,可以同时从多个线程使用。