What is the impact of Thread.Sleep(1) in C#?
在Windows窗体应用程序中,调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public Constructor() { Thread thread = new Thread(Task); thread.IsBackground = true; thread.Start(); } private void Task() { while (true) { // do something Thread.Sleep(1); } } |
该线程会占用所有可用的CPU吗?
我可以使用什么配置技术来测量此线程的CPU使用率(任务管理器除外)?
如前所述,您的循环不会占用CPU。
但是请注意:Windows不是实时操作系统,因此您不会从Thread.Sleep(1)每秒获得1000次唤醒。如果您未使用timeBeginPeriod设置最低分辨率,则大约每15毫秒唤醒一次。即使将最低分辨率设置为1 ms,您仍然只能每3-4 ms唤醒一次。
为了获得毫秒级的计时器粒度,您必须使用Win32多媒体计时器(C#包装器)。
不,它不会占用CPU,它只会使您的线程至少暂停那么长时间。线程暂停时,操作系统可以安排另一个不相关的线程来使用处理器。
如上所述的Thread.Sleep(1)不会占用CPU。
这是线程休眠(或多或少)时发生的情况:
- Thread.Sleep转换为系统调用,进而触发陷阱(使操作系统能够控制的中断)
- 操作系统检测到睡眠调用,并将您的线程标记为已阻止。
- 在内部,操作系统会保留需要唤醒的线程列表以及何时应唤醒线程。
- 由于线程不再使用CPU,因此OS ...
- 如果父进程没有用完所有时间片,则OS将安排该进程的另一个线程执行。
- 否则,另一个进程(或空闲进程)将开始执行。
- 时间到了,您的线程将被再次安排执行,这并不意味着它将自动开始执行。
最后一点,我不完全知道您在做什么,但似乎您正在尝试扮演调度程序的角色,也就是说,要让CPU有时间做其他事情。
在少数情况下(确实很少),可能没问题,但是大多数情况下,您应该让调度程序执行其工作,它可能比您了解的更多,并且可以使工作变得更好。
应避免睡眠时间太短,因为在重新分配信号时,放弃其时间片的线程会获得优先级提升,这可能导致高上下文切换。 在多线程/服务器应用程序中,由于线程在争夺CPU时间,因此这可能导致颠簸效果。 而是依赖异步函数和同步对象,例如关键部分或互斥体/信号量。
正如鲍勃·纳德勒(Bob Nadler)所述,
这是一个使用Win32多媒体计时器强制睡眠1毫秒的示例。
1 2 3 4 5 6 7 8 9 10 11 | [DllImport("winmm.dll")] internal static extern uint timeBeginPeriod(uint period); [DllImport("winmm.dll")] internal static extern uint timeEndPeriod(uint period); timeBeginPeriod(1); while(true) { Thread.Sleep(1); // will sleep 1ms every time } timeEndPeriod(1); |
在C#GUI应用程序中对此进行测试,我发现该应用程序使用了大约50%的CPU。
有关此主题的更多讨论,请参见以下论坛主题:
http://www.dotnet247.com/247reference/msgs/57/289291.aspx
这是我的很多搜索中提到的旧线程,但是Win7有一个新的调度程序,其行为似乎与上面的有所不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { DateTime dtEnd = DateTime.Now.AddSeconds(1.0); int i = 0; while (DateTime.Now < dtEnd) { i++; Thread.Sleep(1); } Console.WriteLine(i.ToString()); i = 0; long lStart = DateTime.Now.Ticks; while (i++ < 1000) Thread.Sleep(1); long lTmp = (DateTime.Now.Ticks - lStart) / 10000; Console.WriteLine(lTmp.ToString()); Console.Read(); } } } |
使用上面的代码,我的第一个结果是946。因此,在1毫秒的时间内使用1ms的睡眠,我得到了946个唤醒。这非常接近1ms。
第二部分询问每1ms进行1000次睡眠事件需要多长时间。我有1034ms。同样,将近1ms。
这是在使用.Net 4.0的1.8ghz core2duo + Win7上
编辑:记住,sleep(x)并不意味着此时醒来,它意味着不早于这次唤醒我。不保证。虽然,您可以增加线程的优先级,并且Windows应该在较低优先级的线程之前安排线程。
不,不会。您几乎看不到它。在每秒不到1000次的某个位置,该线程将唤醒,几乎什么也不做,然后再次休眠。
编辑:
我必须检查一下。在Java 1.5上运行,此测试
1 2 3 4 5 6 7 8 9 10 11 | @Test public void testSpeed() throws InterruptedException { long currentTime = System.currentTimeMillis(); int i = 0; while (i < 1000) { Thread.sleep(1); i++; } System.out.println("Executed in" + (System.currentTimeMillis() - currentTime)); } |
在我的3ghz机器上,每秒大约可运行500个睡眠。我想C#应该差不多一样。我假设有人会用C#编号报告这个非常重要的真实基准。顺便说一句,没有可观察到的CPU使用率。
不,它不会占用所有可用的CPU,因为当另一个线程有工作要做时,睡眠线程将由OS的调度程序关闭。
也看看这个:msdn论坛
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; namespace Test { public static class Program { public static void Main(string[] args) { Stopwatch sw = new Stopwatch(); for (int i = 0; i < 10; ++i) { sw.Reset(); sw.Start(); Thread.Sleep(50); sw.Stop(); Console.WriteLine("(default) Slept for" + sw.ElapsedMilliseconds); TimeBeginPeriod(1); sw.Reset(); sw.Start(); Thread.Sleep(50); sw.Stop(); TimeEndPeriod(1); Console.WriteLine("(highres) Slept for" + sw.ElapsedMilliseconds +"\ "); } } [DllImport("winmm.dll", EntryPoint="timeBeginPeriod", SetLastError=true)] private static extern uint TimeBeginPeriod(uint uMilliseconds); [DllImport("winmm.dll", EntryPoint="timeEndPeriod", SetLastError=true)] private static extern uint TimeEndPeriod(uint uMilliseconds); } } |
一个线程一次最多只能占用一个(逻辑)CPU。而且1毫秒的睡眠不会占用您很多时间。不要流汗。