Thread.Yield()
方法的 C# 实现如下:
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void Yield();
其中:
[MethodImpl(MethodImplOptions.InternalCall)]
特性表示该方法由公共语言运行时 (CLR) 本机代码实现。
Thread.Yield()
方法的本机实现因操作系统而异。在 Windows 上,Thread.Yield()
方法通过调用 SwitchToThread()
函数来实现,该函数将当前线程的状态设置为就绪状态,并将其移到就绪队列的末尾。
在 Linux 上,Thread.Yield()
方法通过调用 sched_yield()
函数来实现,该函数将当前线程的状态设置为可运行状态,并将其移到可运行队列的末尾。
在 macOS 上,Thread.Yield()
方法通过调用 pthread_yield()
函数来实现,该函数将当前线程的状态设置为可运行状态,并将其移到可运行队列的末尾。
Thread.Yield()
方法让出当前线程的 CPU 时间片,允许其他线程运行。这与 Thread.Sleep(0)
类似,但 Thread.Yield()
不保证线程立即让出 CPU 时间片,而 Thread.Sleep(0)
则保证线程立即让出 CPU 时间片。
Thread.Yield()
方法对于需要快速响应的低延迟场景非常有用,例如忙循环、锁自旋和无锁数据结构。它比 Thread.Sleep(0)
更轻量级,并且不会导致线程阻塞或上下文切换。
请注意,Thread.Yield()
方法是一个 CPU 密集型的操作,如果过度使用,可能会导致 CPU 使用率过高。因此,应谨慎使用 Thread.Yield()
方法,并且仅在绝对必要时才使用。
四、例子
private void SpinUntilCompleted() {
if (Task.IsCompleted) {
return;
}
var sw = new SpinWait();
while (!Task.IsCompleted) {
sw.SpinOnce();
}
}
internal bool TrySpinWait()
{
var wait = new SpinWait();
do
{
wait.SpinOnce();
if(Pending.HasResult(_key)) return true;
} while (!wait.NextSpinWillYield);
return false;
}
private void PushCore(Node head, Node tail)
{
SpinWait spinWait = default;
do
{
spinWait.SpinOnce();
tail.m_next = m_head;
}
while (Interlocked.CompareExchange(ref m_head, head, tail.m_next) != tail.m_next);
}
private void PublicationOnlyWaitForOtherThreadToPublish()
{
var spinWait = new SpinWait();
while (!ReferenceEquals(_state, null))
{
// We get here when PublicationOnly temporarily sets _state to LazyHelper.PublicationOnlyWaitForOtherThreadToPublish.
// This temporary state should be quickly followed by _state being set to null.
spinWait.SpinOnce();
}
}
public void Idle(int workCount)
{
if (workCount > 0)
{
return;
}
_spinWait.SpinOnce();
}
public void Wait()
{
SpinWait wait = new SpinWait();
wait.SpinOnce();
while (Count > 0)
Thread.Sleep(1);
}
public T BlockingReadInbound<T>()
{
T msg;
var sw = new SpinWait();
while ((msg = ReadInbound<T>()) == null)
{
sw.SpinOnce();
}
return msg;
}
public static void SpinWaitWhileNull<T>(ref T? check)
where T : class
{
var spinWait = new SpinWait();
while (Volatile.Read(ref check) == null)
{
spinWait.SpinOnce();
}
}
public void Enqueue(T item)
{
SpinWait spin = new SpinWait();
while (true)
{
Segment tail = _tail;
if (tail.TryAppend(item))
return;
spin.SpinOnce();
}
}
public void Add(ThreadPoolWorkItem item)
{
TurboContract.Requires(item != null, conditionString: "item != null");
SpinWait sw = new SpinWait();
Segment tail = _tail;
while (!tail.TryAdd(item))
{
sw.SpinOnce();
tail = _tail;
}
}
[DebuggerStepThrough]
public static void Update(ref int location, Func<int, int> generator)
{
var spinner = new SpinWait();
while (true)
{
var snapshot1 = location;
var value = generator(snapshot1);
var snapshot2 = Interlocked.CompareExchange(ref location, value, snapshot1);
if (snapshot1.Equals(snapshot2)) { return; }
spinner.SpinOnce();
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private long SpinWaitForImpl(long expectedValue, CancellationToken cancellationToken)
{
var spinWait = new SpinWait();
long availableSequence;
do
{
cancellationToken.ThrowIfCancellationRequested();
spinWait.SpinOnce();
availableSequence = Value;
}
while (availableSequence < expectedValue);
return availableSequence;
}