Thread.Yield()导致CPU峰值?

时间:2014-03-08 09:22:02

标签: .net multithreading .net-4.0 yield

在我的一个项目中,我注意到随着连接的客户端数量的增加,服务器的CPU使用量会增加。

  

10个客户:大多数为0%,随机增加到7%   15个客户:大多数为0%,随机增加到10%   25个客户:大多数为10%,随机增加到60%   50个客户:大部分为50%,峰值为60%,CPU总体为100%(由于游戏服务商)   (注意:CPU上有8个逻辑核心)

我将问题缩小到Thread.Yield,在这一行:https://github.com/vercas/vProto/blob/master/vProto/Base%20Client/Package%20Sending.cs#L121
一旦我评论该线路,即使有100个客户端,CPU使用率仍然保持在0%!

为什么Thread.Yield这样做?

2 个答案:

答案 0 :(得分:1)

这是由于Thread.Yield释放处理的方式。它强制当前进程线程过早释放。这反过来会向所有其他进程发送消息,告诉他们自己做的事情。在交换内存,加载缓存进程以及不按顺序移动进程列表方面,切换进程上下文非常昂贵。

来自MSDN

  

如果此方法成功,则会产生线程当前时间片的其余部分。操作系统根据调度线程的优先级和可运行的其他线程的状态,为另一个时间片安排调用线程。

     

Yielding仅限于正在执行调用线程的处理器。即使该处理器空闲或正在运行较低优先级的线程,操作系统也不会将执行切换到另一个处理器。如果没有其他线程准备好在当前处理器上执行,则操作系统不会产生执行,并且此方法返回false。

     

此方法相当于使用平台调用来调用本机Win32 SwitchToThread函数。您应该调用Yield方法而不是使用平台调用,因为平台调用会绕过主机请求的任何自定义线程行为。


<强>更新

Thread.Yield导致昂贵的上下文切换的声明存在一些挑战。以下是其他参考资料:

Difference between Thread.Sleep0 and Thread.Yield

Threading in C# - Joseph Albahari

MSDN - About Processes and Threads

MSDN - Multitasking Considerations

  

建议的准则是尽可能少地使用线程,从而最大限度地减少系统资源的使用。这提高了性能。在设计应用程序时,多任务处理需要考虑资源需求和潜在冲突。资源要求如下:

     
      
  • 系统消耗内存以获取进程和线程所需的上下文信息。因此,可以创建的进程和线程数受可用内存的限制。
  •   
  • 跟踪大量线程会占用大量处理器时间。如果线程太多,大多数线程将无法取得重大进展。如果大多数当前线程都在一个进程中,则其他进程中的线程调度次数较少。
  •   

答案 1 :(得分:1)

不知道为什么Thread.Yield / Sleep 1 的使用可能会导致这些峰值,但我反驳说它只是由“上下文切换”引起的。 (我毫不怀疑它有关系,但需要更强有力的解释。)

Thread.Sleep or Thread.Yield似乎给出了一个令人满意的答案,当使用完全的时候产生和睡眠时 - 基本上这样的产量,如睡眠(0),可能产量 - 虽然它可能不会直接应用“产品和睡眠,如果需要” 1 vs“总是睡觉而不试图产量”,如本问题所示。

1 使用的原始CPU加标代码:if (!Thread.Yield()) Thread.Sleep(10);。 (这是为什么重要将相关代码包含在问题中的一个例子。)

我的论据反对上下文切换是问题所在。


  1. Windows使用preemptive scheduling和上下文切换每秒几十次,即使线程没有主动屈服。

  2. Thread.Sleep(x),其中x&gt; 0,总是 cause a context-switch;但{em> 报告Thread.Sleep(1)导致此类峰值。

  3. Thread.Yield 可能导致上下文切换,但据报道会导致峰值。

      

    操作系统(读取:Thread.Yield)将切换执行,如果..