.net 切换到线程/Thread.Yield vs.Thread.Sleep(0)vs.Thead.Sleep(1)
我正在尝试编写最终的“Yield”方法,以便将当前的时间片提供给其他线程。到目前为止,我发现有几种不同的方法可以使线程产生分配的时间片。我只是想确保我正确地解释了它们,因为文档不是很清楚。因此,从我对stackoverflow、MSDN和各种博客文章的了解来看,以下选项都有不同的优点/缺点:.net 切换到线程/Thread.Yield vs.Thread.Sleep(0)vs.Thead.Sleep(1),.net,multithreading,concurrency,.net,Multithreading,Concurrency,我正在尝试编写最终的“Yield”方法,以便将当前的时间片提供给其他线程。到目前为止,我发现有几种不同的方法可以使线程产生分配的时间片。我只是想确保我正确地解释了它们,因为文档不是很清楚。因此,从我对stackoverflow、MSDN和各种博客文章的了解来看,以下选项都有不同的优点/缺点: SwitchToThread
SwitchToThread
Thread.Yield
- 优点:速度大约是原来的两倍
Thread.Sleep(0)
- 缺点:只允许线程使用 在同一处理器上
线程。睡眠(0)
:让位于任何处理器上具有相同或更高优先级的任何线程
- 优点:比以前快
Thread.Sleep(1)
- 缺点:只允许线程使用 同等或更高优先级的
Thread.Sleep(1)
:让位于任何处理器上的任何线程
- 优点:对任何线程都有让步 任何处理器
- 缺点:最慢的选择
(
通常 如果需要,将螺纹悬挂约15毫秒Thread.Sleep(1)
/timeBeginPeriod
[win32]未使用)timeEndPeriod
public static class Thread
{
[DllImport("kernel32.dll")]
static extern bool SwitchToThread();
[DllImport("winmm.dll")]
internal static extern uint timeBeginPeriod(uint period);
[DllImport("winmm.dll")]
internal static extern uint timeEndPeriod(uint period);
/// <summary> yields time slice of current thread to specified target threads </summary>
public static void YieldTo(ThreadYieldTarget threadYieldTarget)
{
switch (threadYieldTarget) {
case ThreadYieldTarget.None:
break;
case ThreadYieldTarget.AnyThreadOnAnyProcessor:
timeBeginPeriod(1); //reduce sleep to actually 1ms instead of system time slice with is around 15ms
System.Threading.Thread.Sleep(1);
timeEndPeriod(1); //undo
break;
case ThreadYieldTarget.SameOrHigherPriorityThreadOnAnyProcessor:
System.Threading.Thread.Sleep(0);
break;
case ThreadYieldTarget.AnyThreadOnSameProcessor:
SwitchToThread();
break;
default: throw new ArgumentOutOfRangeException("threadYieldTarget");
}
}
}
public enum ThreadYieldTarget
{
/// <summary> Operation system will decide when to interrupt the thread </summary>
None,
/// <summary> Yield time slice to any other thread on any processor </summary>
AnyThreadOnAnyProcessor,
/// <summary> Yield time slice to other thread of same or higher piority on any processor </summary>
SameOrHigherPriorityThreadOnAnyProcessor,
/// <summary> Yield time slice to any other thread on same processor </summary>
AnyThreadOnSameProcessor
}
公共静态类线程
{
[DllImport(“kernel32.dll”)]
静态外部bool SwitchToThread();
[DllImport(“winmm.dll”)]
内部静态外部单元时间起始周期(单元周期);
[DllImport(“winmm.dll”)]
内部静态外部uint timeEndPeriod(uint period);
///将当前线程的时间片生成到指定的目标线程
公共静态无效YieldTo(ThreadYieldTarget ThreadYieldTarget)
{
交换机(ThreadyEldTarget){
案例ThreadyEldTarget。无:
打破
case ThreadyEldTarget.AnyThreadOnAnyProcessor:
timeBeginPeriod(1);//将睡眠时间减少到实际1ms,而不是系统时间片为15ms左右
系统线程线程睡眠(1);
timeEndPeriod(1);//撤消
打破
case ThreadyEldTarget.SameorhigherPriorityThreadOnAny处理器:
系统线程线程睡眠(0);
打破
case ThreadyEldTarget.AnyThreadOnSameProcessor:
SwitchToThread();
打破
默认值:抛出新ArgumentOutOfRangeException(“threadYieldTarget”);
}
}
}
公共枚举ThreadYieldTarget
{
///操作系统将决定何时中断线程
没有一个
///将时间片让给任何处理器上的任何其他线程
任何线程,任何处理器,
///在任何处理器上,将时间片提供给具有相同或更高优先级的其他线程
SameorhigherpriorityThreadOnAny处理器,
///将时间片让给同一处理器上的任何其他线程
AnyThreadOnSameProcessor
}
SpinWait设计为在不产生当前时间片的情况下等待
它是为那些你知道你想在很短的时间内完成某件事的情况而设计的,所以失去时间片将是过度的
我的印象是线程。任何x值的收益率(x)<线程量是等价的,包括零,尽管我没有这方面的基准。SpinWait在超线程处理器上很有用。通过超线程,可以在同一物理处理器上运行多个OS调度线程,共享处理器资源。SpinWait向处理器指示您没有做任何有用的工作,它应该从不同的逻辑CPU运行代码。顾名思义,它通常在旋转时使用 假设您有如下代码:
while (!foo) {} // Spin until foo is set.
如果此线程在超线程处理器上的线程上运行,则它正在消耗可用于处理器上运行的其他线程的处理器资源
改为:
while (!foo) {Thread.SpinWait(1);}
我们指示CPU向另一个线程提供一些资源
SpinWait不会影响线程的操作系统调度
对于你关于“最终收益率”的主要问题,这在很大程度上取决于你的情况——如果你不澄清为什么你想让一条线收益,你将无法得到一个好的答案。在我看来,让处理器运行的最好方法是让线程进入等待状态,只有在有工作要做时才唤醒。其他任何事情都只是浪费CPU时间。Jeff Moser()的文章“锁是如何锁定的”可以提供一些关于SpinWait机制的内部信息。引用该文件:
它到底在做什么?看着
转子的
clr/src/vm/comsynchronizable.cpp提供
美国面对现实:
FCIMPL1(void,ThreadNative::SpinWait,int迭代)
{
包装合同;
静态契约容忍
for(int i = 0; i < iterations; i++)
YieldProcessor();
for(int i=0;i
}
FCIMPLEND
进一步的潜水表明
“YieldProcessor”是一个宏:
#定义YieldProcessor()
这是一个“重复无操作”组件
指示这在世界上也是众所周知的
英特尔指令集手册为“暂停”
-这意味着CPU知道等待它的自旋
我们想要完成
相关的:
除了其他答案,这里还有一些分析数据 (!)不要太认真地对待这个评测!这只是为了用数字说明上述答案,并粗略比较magnitu
static void Profile(Action func)
{
var sw = new Stopwatch();
var beginTime = DateTime.Now;
ulong count = 0;
while (DateTime.Now.Subtract(beginTime).TotalSeconds < 5)
{
sw.Start();
func();
sw.Stop();
count++;
}
Console.WriteLine($"Made {count} iterations in ~5s. Total sleep time {sw.ElapsedMilliseconds}[ms]. Mean time = {sw.ElapsedMilliseconds/(double) count} [ms]");
}
Profile(()=>Thread.Sleep(0));
Profile(()=>Thread.Sleep(1));
Profile(()=>Thread.Yield());
Profile(()=>Thread.SpinWait(1));
Function | CPU % | Iters made | Total sleep | Invoke
| | | time [ms] | time [ms]
=====================================================================
Sleep(0) | 100 0 | 2318103 | 482 | 0.00020
Sleep(1) | 6 0 | 4586 | 5456 | 1.08971
Yield() | 100 0 | 2495220 | 364 | 0.00010
SpinWait(1)| 100 0 | 2668745 | 81 | 0.00003