Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
System.Threading.SpinWait — это упрощенный тип синхронизации, который можно использовать в низкоуровневых сценариях, чтобы избежать дорогостоящих переключений контекста и переходов ядра, необходимых для событий ядра. На многопроцессорных компьютерах, когда ресурс не предполагается удерживаться в течение длительного времени, более эффективно для потока в режиме пользователя ожидать несколько десятков или несколько сотен циклов, а затем повторно попытаться получить ресурс. Если ресурс доступен после спиннинга, вы сохранили несколько тысяч циклов. Если ресурс по-прежнему недоступен, это заняло всего несколько циклов, и вы всё ещё можете перейти в режим ожидания, основанный на ядре. Эта комбинация активного ожидания, затем пассивного ожидания иногда называется двухэтапной операцией ожидания.
SpinWait предназначен для использования в сочетании с типами .NET, которые упаковывают события ядра, такие как ManualResetEvent. SpinWait также можно использовать самостоятельно для базовых функций спиннинга в одной программе.
SpinWait это больше, чем просто пустой цикл. Он тщательно реализуется, чтобы обеспечить правильное поведение спиннинга для общего случая, и сам инициирует переключения контекста, если он работает достаточно долго (примерно продолжительность времени, необходимого для перехода ядра). Например, на одноядерных компьютерах SpinWait возвращает срез времени потока немедленно, так как спиннинг блокирует ход выполнения всех потоков.
SpinWait также уступает даже на многопроцессорных системах, чтобы предотвратить блокировку ожидающих потоков с более высоким приоритетом или сборщика мусора. Поэтому, если вы используете SpinWait в двухэтапной операции ожидания, мы рекомендуем вызвать ожидание ядра, прежде чем SpinWait сам инициирует переключение контекста.
SpinWait предоставляет свойство NextSpinWillYield, которое можно проверить перед каждым вызовом SpinOnce. Когда свойство возвращается true
, инициируйте собственную операцию ожидания. Пример см. в статье "Практическое руководство. Использование SpinWait для реализации операции ожидания Two-Phase".
Если вы не выполняете двухэтапную операцию ожидания, а просто повторяете операцию в цикле, пока не будет выполнено некоторое условие, вы можете включить SpinWait, чтобы выполнялись его переключения контекста и он вел себя корректно в среде операционной системы Windows. В следующем базовом примере показан SpinWait в стеке без использования блокировок. Если требуется высокопроизводительный потокобезопасный стек, рассмотрите возможность использования System.Collections.Concurrent.ConcurrentStack<T>.
public class LockFreeStack<T>
{
private volatile Node m_head;
private class Node { public Node Next; public T Value; }
public void Push(T item)
{
var spin = new SpinWait();
Node node = new Node { Value = item }, head;
while (true)
{
head = m_head;
node.Next = head;
if (Interlocked.CompareExchange(ref m_head, node, head) == head) break;
spin.SpinOnce();
}
}
public bool TryPop(out T result)
{
result = default(T);
var spin = new SpinWait();
Node head;
while (true)
{
head = m_head;
if (head == null) return false;
if (Interlocked.CompareExchange(ref m_head, head.Next, head) == head)
{
result = head.Value;
return true;
}
spin.SpinOnce();
}
}
}
Imports System.Threading
Module SpinWaitDemo
Public Class LockFreeStack(Of T)
Private m_head As Node
Private Class Node
Public [Next] As Node
Public Value As T
End Class
Public Sub Push(ByVal item As T)
Dim spin As New SpinWait()
Dim head As Node, node As New Node With {.Value = item}
While True
Thread.MemoryBarrier()
head = m_head
node.Next = head
If Interlocked.CompareExchange(m_head, node, head) Is head Then Exit While
spin.SpinOnce()
End While
End Sub
Public Function TryPop(ByRef result As T) As Boolean
result = CType(Nothing, T)
Dim spin As New SpinWait()
Dim head As Node
While True
Thread.MemoryBarrier()
head = m_head
If head Is Nothing Then Return False
If Interlocked.CompareExchange(m_head, head.Next, head) Is head Then
result = head.Value
Return True
End If
spin.SpinOnce()
End While
End Function
End Class
End Module