我正在尝试编写最终的“Yield”方法,以将当前时间片提供给其他线程。到目前为止,我发现有几种不同的方法可以使线程产生分配的时间片。我只是想确保我正确地解释它们,因为文档不是很清楚。因此,根据我在stackoverflow,MSDN和各种博客文章中所读到的内容,存在以下选项,它们都有不同的优点/缺点:
SwitchToThread
[win32] / Thread.Yield
[.NET 4 Beta 1]:产生同一处理器上的任何线程
Thread.Sleep(0)
Thread.Sleep(0)
:会在任何处理器上产生相同或更高优先级的任何线程
Thread.Sleep(1)
Thread.Sleep(1)
:屈服于任何处理器上的任何线程
Thread.Sleep(1)
通常会
如果,挂起约15毫秒的线程
timeBeginPeriod
/ timeEndPeriod
[win32]未使用) Thread.SpinWait
怎么样?可以用它来产生线程的时间片吗?如果没有,它用于什么?
我还有其他一些我错过或错误解释的东西。如果你能纠正/增加我的理解,我将不胜感激。
这就是我的Yield方法到目前为止的样子:
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
}
答案 0 :(得分:12)
SpinWait在超线程处理器上很有用。通过超线程,多个OS调度线程可以在同一物理处理器上运行,共享处理器资源。 SpinWait向处理器指示您没有做任何有用的工作,并且它应该从不同的逻辑CPU运行代码。顾名思义,它通常在您旋转时使用。
假设您有以下代码:
while (!foo) {} // Spin until foo is set.
如果此线程在超线程处理器上的线程上运行,则会消耗可用于处理器上运行的其他线程的处理器资源。
改为:
while (!foo) {Thread.SpinWait(1);}
我们正在向CPU指示为其他线程提供一些资源。
SpinWait不会影响线程的OS调度。
关于“终极收益”的主要问题,这在很大程度上取决于您的情况 - 如果没有说明您希望线程产生的原因,您将无法得到一个好的答案。从我的角度来看,产生处理器的最好方法是让线程进入等待状态,只有在有工作要做时才会醒来。其他任何事情只是在浪费CPU时间。
答案 1 :(得分:5)
Jeff Moser撰写的文章“Locks Lock如何”(http://www.moserware.com/2008/09/how-do-locks-lock.html)可以给出一些关于SpinWait机制的内容。引用该文件:
究竟是做什么的?看着 转子 clr / src / vm / comsynchronizable.cpp给出 我们现实:
FCIMPL1(void,ThreadNative :: SpinWait,int iterations) { WRAPPER_CONTRACT; STATIC_CONTRACT_SO_TOLERANT;
for(int i = 0; i < iterations; i++) YieldProcessor();
} FCIMPLEND
进一步潜水表明 “YieldProcessor”就是这个宏:
#define YieldProcessor()__ asm {rep nop}
这是一个“重复无操作”程序集 指令。它也是众所周知的 英特尔指令集手册为“PAUSE - Spin Loop Hint。“这意味着CPU知道旋转等待 我们想要完成。
相关: http://msdn.microsoft.com/en-us/library/ms687419(VS.85).aspx http://www.moserware.com/2008/09/how-do-locks-lock.html#lockfn7
答案 2 :(得分:4)
SpinWait设计为等待而不产生当前时间片
它适用于你知道你想在很短的时间内做某事的情况,所以失去你的时间片会过度。
对于任何x&lt;的值,我都在Thread.Yield(x)的印象之下。线程量子是等价的,包括零,虽然我没有这个效果的基准。
答案 3 :(得分:2)
除了其他答案,这里还有一些分析数字。
(!)不要太认真地对待这个分析!仅仅是为了在数字上说明上述答案并粗略地比较值的大小。
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));
旋转循环的结果~5s:
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
使用Mono 4.2.3 x86_64制作