SpinLock Estructura
Definición
Importante
Parte de la información hace referencia a la versión preliminar del producto, que puede haberse modificado sustancialmente antes de lanzar la versión definitiva. Microsoft no otorga ninguna garantía, explícita o implícita, con respecto a la información proporcionada aquí.
Proporciona una primitiva de bloqueo de exclusión mutua donde un subproceso que intenta adquirir el bloqueo espera en un bucle y realiza comprobaciones repetidamente hasta que haya un bloqueo disponible.
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
- Herencia
- Atributos
Ejemplos
En el ejemplo siguiente se muestra cómo usar :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
Comentarios
Para obtener un ejemplo de cómo usar un bloqueo de número, vea Cómo: Usar SpinLock para Low-Level sincronización.
Los bloqueos de número se pueden usar para bloqueos de nivel hoja en los que la asignación de objetos implícita mediante un , en tamaño o debido a la Monitorpresión de recolección de elementos no utilizados, es demasiado costosa. Un bloqueo de giro puede ser útil para evitar el bloqueo; sin embargo, si espera una cantidad significativa de bloqueo, probablemente no debe usar bloqueos de giro debido a un giro excesivo. El giro puede ser beneficioso cuando los bloqueos son específicos y grandes en número (por ejemplo, un bloqueo por nodo en una lista vinculada) y también cuando los tiempos de suspensión de bloqueo siempre son extremadamente cortos. En general, al mantener un bloqueo de número, uno debe evitar cualquiera de estas acciones:
Bloqueo
llamar a cualquier cosa que pueda bloquearse,
mantener más de un bloqueo de giro a la vez,
realizar llamadas enviadas dinámicamente (interfaz y virtuales),
realizar llamadas enviadas estáticamente a cualquier código que no posee, o
asignar memoria.
SpinLock solo se debe usar después de haber determinado que, al hacerlo, se mejorará el rendimiento de una aplicación. También es importante tener en cuenta que SpinLock es un tipo de valor, por motivos de rendimiento. Por este motivo, debe tener mucho cuidado de no copiar accidentalmente una SpinLock instancia, ya que las dos instancias (original y la copia) serían completamente independientes entre sí, lo que probablemente provocaría un comportamiento erróneo de la aplicación. Si se debe pasar una SpinLock instancia de , se debe pasar por referencia en lugar de por valor.
No almacene SpinLock instancias en campos de solo lectura.
Constructores
SpinLock(Boolean) |
Inicializa una nueva instancia de la estructura SpinLock con la opción de realizar el seguimiento de los identificadores de subprocesos para mejorar la depuración. |
Propiedades
IsHeld |
Obtiene un valor que indica si un subproceso mantiene actualmente el bloqueo. |
IsHeldByCurrentThread |
Obtiene un valor que indica si el subproceso actual mantiene actualmente el bloqueo. |
IsThreadOwnerTrackingEnabled |
Obtiene un valor que indica si el seguimiento de propiedad de subprocesos está habilitado para esta instancia. |
Métodos
Enter(Boolean) |
Adquiere el bloqueo de manera confiable de modo que, incluso si se produce una excepción en la llamada al método, se pueda examinar |
Exit() |
Libera el bloqueo. |
Exit(Boolean) |
Libera el bloqueo. |
TryEnter(Boolean) |
Intenta adquirir el bloqueo de manera confiable de modo que, incluso si se produce una excepción en la llamada al método, se pueda examinar |
TryEnter(Int32, Boolean) |
Intenta adquirir el bloqueo de manera confiable de modo que, incluso si se produce una excepción en la llamada al método, se pueda examinar |
TryEnter(TimeSpan, Boolean) |
Intenta adquirir el bloqueo de manera confiable de modo que, incluso si se produce una excepción en la llamada al método, se pueda examinar |
Se aplica a
Seguridad para subprocesos
Todos los miembros de SpinLock son seguros para subprocesos y se pueden usar de varios subprocesos simultáneamente.