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
설명
스핀 잠금을 사용하는 방법에 대한 예제는 방법: Low-Level 동기화에 SpinLock 사용 방법을 참조하세요.
스핀 잠금은 크기 또는 가비지 수집 압력으로 인해 개체 할당이 암시되는 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 스레드로부터 안전하며 여러 스레드에서 동시에 사용할 수 있습니다.