Sleep()简析 和Sleep(0)的妙用

本文深入解析了Sleep(0)在多线程编程中的作用,揭示了其如何通过微小的线程让位,提高程序整体效率,尤其是在Windows抢占式操作系统环境下。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Sleep()简析 和Sleep(0)的妙用

原贴地址:残月:Thread.sleep(0)的意义
     HawkJony:Sleep(0)的妙用

Thread.Sleep(0) 表示挂起0毫秒,你可能觉得没作用,你要写Thread.Sleep(1000) 就有感觉了。似乎毫无意义。
MSDN的说明:指定零 (0) 以指示应挂起此线程以使其他等待线程能够执行。

Thread.Sleep(0) 并非是真的要线程挂起0毫秒,意义在于这次调用Thread.Sleep(0)的当前线程确实的被冻结了一下,让其他线程有机会优先执行。Thread.Sleep(0) 是你的线程暂时放弃cpu,也就是释放一些未用的时间片给其他线程或进程使用,就相当于一个让位动作。



Thread th = new Thread(new ThreadStart(MainForm.StartSplash));
th.Priority = ThreadPriority.AboveNormal;
th.Start();
Thread.Sleep(0);
 
base.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
this.Initialize();
在线程中,调用sleep(0)可以释放cpu时间,让线程马上重新回到就绪队列而非等待队列,sleep(0)释放当前线程所剩余的时间片(如果有剩余的话),这样可以让操作系统切换其他线程来执行,提升效率。

我们可能经常会用到 Thread.Sleep 函数来使线程挂起一段时间。那么你有没有正确的理解这个函数的用法呢?

思考下面这两个问题:

  1. 假设现在是 2017-4-7 12:00:00.000,如果我调用一下 Thread.Sleep(1000) ,在 2017-4-7
    12:00:01.000 的时候,这个线程会 不会被唤醒?
  2. 某人的代码中用了一句看似莫明其妙的话:Thread.Sleep(0) 。既然是 Sleep 0
    毫秒,那么他跟去掉这句代码相比,有啥区别么?




我们先回顾一下操作系统原理.

操作系统中,CPU竞争有很多种策略。Unix系统使用的是时间片算法,而Windows则属于抢占式的。

时间片算法中,所有的进程排成一个队列。操作系统按照他们的顺序,给每个进程分配一段时间,即该进程允许运行的时间。如果在 时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。调度程 序所要做的就是维护一张就绪进程列表,,当进程用完它的时间片后,它被移到队列的末尾。

所谓抢占式操作系统,就是说如果一个进程得到了 CPU 时间,除非它自己放弃使用 CPU ,否则将完全霸占 CPU 。因此可以看出,在抢 占式操作系统中,操作系统假设所有的进程都是“人品很好”的,会主动退出 CPU 。在抢占式操作系统中,假设有若干进程,操作系统会根据他们的优先级、饥饿时间(已经多长时间没有使用过 CPU 了),给他们算出一 个总的优先级来。操作系统就会把 CPU 交给总优先级最高的这个进程。当进程执行完毕或者自己主动挂起后,操作系统就会重新计算一 次所有进程的总优先级,然后再挑一个优先级最高的把 CPU 控制权交给他。

我们用分蛋糕的场景来描述这两种算法。假设有源源不断的蛋糕(源源不断的时间),一副刀叉(一个CPU),10个等待吃蛋糕的人(10 个进程)。

如果是 Unix操作系统来负责分蛋糕,那么他会这样定规矩:每个人上来吃 1 分钟,时间到了换下一个。最后一个人吃完了就再从头开始。于是,不管这10个人是不是优先级不同、饥饿程度不同、饭量不同,每个人上来的时候都可以吃 1 分钟。当然,如果有人本来不太饿,或者饭量小,吃了30秒钟之后就吃饱了,那么他可以跟操作系统说:我已经吃饱了(挂起)。于是操作系统就会让下一个人接着来。

如果是 Windows 操作系统来负责分蛋糕的,那么场面就很有意思了。他会这样定规矩:我会根据你们的优先级、饥饿程度去给你们每个人计算一个优先级。优先级最高的那个人,可以上来吃蛋糕——吃到你不想吃为止。等这个人吃完了,我再重新根据优先级、饥饿程度来计算每个人的优先级,然后再分给优先级最高的那个人。

这样看来,这个场面就有意思了——可能有些人是PPMM,因此具有高优先级,于是她就可以经常来吃蛋糕。可能另外一个人是个丑男,而去很ws,所以优先级特别低,于是好半天了才轮到他一次(因为随着时间的推移,他会越来越饥饿,因此算出来的总优先级就会越来越高,因此总有一天会轮到他的)。而且,如果一不小心让一个大胖子得到了刀叉,因为他饭量大,可能他会霸占着蛋糕连续吃很久很久,导致旁边的人在那里咽口水。。。

而且,还可能会有这种情况出现:操作系统现在计算出来的结果,5号PPMM总优先级最高,而且高出别人一大截。因此就叫5号来吃蛋糕。5号吃了一小会儿,觉得没那么饿了,于是说“我不吃了”(挂起)。因此操作系统就会重新计算所有人的优先级。因为5号刚刚吃过,因此她的饥饿程度变小了,于是总优先级变小了;而其他人因为多等了一会儿,饥饿程度都变大了,所以总优先级也变大了。不过这时候仍然有可能5号的优先级比别的都高,只不过现在只比其他的高一点点——但她仍然是总优先级最高的啊。因此操作系统就会说:5号mm上来吃蛋糕……(5号mm心里郁闷,这不刚吃过嘛……人家要减肥……谁叫你长那么漂亮,获得了那么高的优先级)。

那么,Thread.Sleep 函数是干吗的呢?还用刚才的分蛋糕的场景来描述。上面的场景里面,5号MM在吃了一次蛋糕之后,觉得已经有8分饱了,她觉得在未来的半个小时之内都不想再来吃蛋糕了,那么她就会跟操作系统说:在未来的半个小时之内不要再叫我上来吃蛋糕了。这样,操作系统在随后的半个小时里面重新计算所有人总优先级的时候,就会忽略5号mm。Sleep函数就是干这事的,他告诉操作系统“在未来的多少毫秒内我不参与CPU竞争”。




看完了 Thread.Sleep 的作用,我们再来想想文章开头的两个问题。

对于第一个问题,答案是:不一定。

因为你只是告诉操作系统:在未来的1000毫秒内我不想再参与到CPU竞争。那么1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束;况且,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。与此相似的,Thread有个Resume函数,是用来唤醒挂起的线程的。好像上面所说的一样,这个函数只是“告诉操作系统我从现在起开始参与CPU竞争了”,这个函数的调用并不能马上使得这个线程获得CPU控制权。

对于第二个问题,答案是:有,而且区别很明显。

假设我们刚才的分蛋糕场景里面,有另外一个PPMM 7号,她的优先级也非常非常高(因为非常非常漂亮),所以操作系统总是会叫道她来吃蛋糕。而且,7号也非常喜欢吃蛋糕,而且饭量也很大。不过,7号人品很好,她很善良,她没吃几口就会想:如果现在有别人比我更需要吃蛋糕,那么我就让给他。因此,她可以每吃几口就跟操作系统说:我们来重新计算一下所有人的总优先级吧。不过,操作系统不接受这个建议——因为操作系统不提供这个接口。于是7号mm就换了个说法:“在未来的0毫秒之内不要再叫我上来吃蛋糕了”。这个指令操作系统是接受的,于是此时操作系统就会重新计算大家的总优先级——注意这个时候是连7号一起计算的,因为“0毫秒已经过去了”嘛。因此如果没有比7号更需要吃蛋糕的人出现,那么下一次7号还是会被叫上来吃蛋糕。

因此,Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”。竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。这也是我们在大循环里面经常会写一句Thread.Sleep(0) ,因为这样就给了其他线程比如Paint线程获得CPU控制权的权力,这样界面就不会假死在那里。

末了说明一下,虽然上面提到说“除非它自己放弃使用 CPU ,否则将完全霸占 CPU”,但这个行为仍然是受到制约的——操作系统会监控你霸占CPU的情况,如果发现某个线程长时间霸占CPU,会强制使这个线程挂起,因此在实际上不会出现“一个线程一直霸占着 CPU 不放”的情况。至于我们的大循环造成程序假死,并不是因为这个线程一直在霸占着CPU。实际上在这段时间操作系统已经进行过多次CPU竞争了,只不过其他线程在获得CPU控制权之后很短时间内马上就退出了,于是就又轮到了这个线程继续执行循环,于是就又用了很久才被操作系统强制挂起。。。因此反应到界面上,看起来就好像这个线程一直在霸占着CPU一样。

末了再说明一下,文中线程、进程有点混乱,其实在Windows原理层面,CPU竞争都是线程级的,本文中把这里的进程、线程看成同一个东西就好了。



问题:主动的放弃运行让系统调度的意义是什么呢?

为了等待资源、事件,那么你需要进入等待队列。如果你已经拥有运行所需资源,却让系统调度,这是资源的浪费,并且调度也是要浪费资源的

解释:对的,你要等待资源,你确实需要排队,假如AB两个线程为合作关系,A线程处理一些原始数据,数据处理到一定程度,交给B线程处理,在A处理原始数据的时候,B也要做一些准备工作,所以,AB是并发的,但是B做好准备之后,需要等待A处理好那些数据,接过A的数据,继续处理,因此,这个等待,如果A不使用信号或者等待条件来通知B的话,那么B必须一直轮询,查看A是否已完成,B线程所做的这个轮询是否会一直占用CPU来做无用的循环查看呢?因此B这个时候占用的cpu时间片做的是无用功,因此,这里sleep(0)就有作用,当B查看A没处理完数据的时候,B马上sleep(0)交出B的时间片,让操作系统调度A来运行(假设只有AB两个线程),那么这个时候,A就会得到充分的时间来处理它的数据,这个不是一个应用了吗?我猜测pthread_conn_wait()内部阻塞就是使用这个机制

thread_fun()
{
    prepare_word.....
    while (1)
    {
        if (A is finish)
            break;
        else
            sleep(0); //这里会交出B的时间片,下一次调度B的时候,接着执行这个循环
    }
    process A's data
}

没有sleep(0)版:

thread_fun()
{
    prepare_word.....
    while (1)  //这里会一直浪费CPU时间做死循环的轮询,无用功
    {
        if (A is finish)
            break;
    }
    process A's data
}

如果说是轮询,那它就是一种高效、节约、谦虚的轮询,如果没有sleep(0),那么B线程可能会执行上万次的while循环,直至它的时间片消耗完,做这些都是无用功,而是用了sleep(0)后,B线程每一次执行就只做一次while循环就把剩余的时间片让出给A,能让A得到更多的执行次数,利用率更高


总结:

在线程没退出之前,线程有三个状态,就绪态,运行态,等待态。sleep(n)之所以在n秒内不会参与CPU竞争,是因为,当线程调用sleep(n)的时候,线程是由运行态转入等待态,线程被放入等待队列中,等待定时器n秒后的中断事件,当到达n秒计时后,线程才重新由等待态转入就绪态,被放入就绪队列中,等待队列中的线程是不参与cpu竞争的,只有就绪队列中的线程才会参与cpu竞争,所谓的cpu调度,就是根据一定的算法(优先级,FIFO等。。。),从就绪队列中选择一个线程来分配cpu时间。

而sleep(0)之所以马上回去参与cpu竞争,是因为调用sleep(0)后,因为0的原因,线程直接回到就绪队列,而非进入等待队列,只要进入就绪队列,那么它就参与cpu竞争。

<think>首先,用户的问题是关于_sleepsleep函数的用法。用户指定了使用_sleepsleep函数来编程语言。我应该参考用户提供的引用。回顾用户提供的引用:-引用[1]:涉及Python中sleep函数的用法,但提到了PHP中的sleep函数。-引用[2]:关于Linux中sleep函数的使用总结,指出sleep函数是可以被中断的。-引用[3]:关于C++中sleep方法的总结,提到使用<unistd.h>头文件,并警告sleep会阻塞当前线程。用户的问题集中在“_sleepsleep函数”,但我注意到引用中没有具体提到“_sleep”。这可能是一个错误或不规范的形式;在编程中,sleep函数通常是标准函数名,而_sleep可能是一个特定平台或方言中的函数。例如,在WindowsAPI中,有Sleep函数,首字母大写。回想一下:-在Python中,time.sleep(seconds)是常见的。-在C/C++中,标准库有sleep(unsignedintseconds)in<unistd.h>forUnix-likesystems,但Windows有Sleep(DWORDmilliseconds)in<windows.h>.用户说“_sleepsleep函数”,可能指的是不同语言的函数。我应该澄清一下常见的sleep函数。引用中没有明确指出“_sleep”,但从上下文看,可能是用户误写或指代特定函数。我应该假设用户想知道在编程中使用sleep函数的基本方法。系统指令要求我:-所有行内数学表达式使用$...$。-独立公式使用$$...$$。-使用中文回答。-生成相关问题。-在引用末尾自然地添加引用标识。用户说:“我想了解_sleepsleep函数的用法请问如何使用_sleepsleep函数编程语言”,所以用户想知道在编程语言中如何使用这些函数。可能需要解释不同语言中的sleep函数。我的上一句回答是用户提供的引用,不是我的回答。指令说:“尽可能多的参考用户上一次的问题你的回答。”但这是对话的开始,所以可能没有之前的回答。用户说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题你的回答。”但在这个上下文中,用户上一次的问题就是这个,我应该聚焦于此。结构化我的回答:1.**理解问题**:用户想知道如何使用_sleepsleep函数在编程语言中。2.**解释sleep函数**:sleep函数用于在程序中暂停执行指定的时间。常见于多种语言:-Python:time.sleep(seconds)-C/C++:sleep(seconds)in<unistd.h>forUnix,Sleep(milliseconds)in<windows.h>forWindows.-Java:Thread.sleep(milliseconds)-等等。用户提到“_sleep”,可能指的是Windows中的Sleep函数(因为WindowsAPI函数通常首字母大写,但有时在代码中写成_sleep或不规范)。在Windows中,Sleep函数定义在<windows.h>。从引用[3],它提到了C++中使用<unistd.h>,但Windows不同。3.**区分_sleepsleep**:-在一些上下文中,_sleep可能是sleep的别名或特定实现。例如,在一些WindowsAPI文档或代码中,Sleep是标准,但可能有误写。我应该假设“_sleep”可能意为Sleep函数在Windows。从用户引用:-引用[1]:Pythonsleep-引用[2]:Linuxsleep(可以被中断)-引用[3]:C++sleep没有直接提到_sleep。但用户说“_sleepsleep函数”,所以我需要覆盖两个可能的名字。常见场景:在C/C++中:-Unix-like:sleep()in<unistd.h>-Windows:Sleep()in<windows.h>(注意大写S)所以_sleep可能是个笔误或指Sleep函数。我将解释常见sleep函数用法。4.**使用中文回答**:所有回答用中文。5.**包括数学格式**:问题中没有数学表达,但如果有方程等,用$...$或$$...$$。但sleep函数本身没有数学,所以可能不需要,除非举例时间计算。6.**结构清晰**:逐步解释:-首先介绍什么是sleep函数。-然后,在不同编程语言中如何使用:-Python-C/C++-Java-其他特别强调_sleepsleep。-注意事项:如sleep可能被中断,阻塞当前线程等。7.**参考引用**:在回答中自然地添加引用标识。例如,当提到可中断性时,引用[2]。8.**生成相关问题**:结束时,添加相关问题部分。可能的回答结构:1.**解释sleep函数**:sleep函数用于在程序中暂停执行一段指定的时间。2.**常见编程语言中的用法**:-**Python**:使用time模块中的sleep函数。示例代码。-引用[1]谈到Pythonsleep。-**C/C++**:在Unix中使用sleep函数,需包含<unistd.h>;在Windows中使用Sleep函数(带大写S),需包含<windows.h>。解释_sleep可能指WindowsSleep。-引用[3]提到C++sleep。-其他语言如Java。3.**注意事项**:如Linux中sleep可被信号中断(引用[2]),或线程阻塞问题(引用[3])。4.**示例代码**:用代码块展示用法。5.**总结**。6.**相关问题**。现在,关于“_sleep”:我认为它很可能是指Windows中的Sleep函数,因为在一些文档或代码中,有人可能写成_sleep,但不规范。在Windows中,Sleep函数的声明是:-voidSleep(DWORDmilliseconds);而标准是Sleep,首字母大写。在回答中,我可以说:"在Windows编程中,常用的sleep函数是Sleep(注意首字母大写),定义在<windows.h>中。"用户写了“_sleep”,所以可能是用户误写。我将假设_sleep是指类似sleep的函数。写回答。</think> ### sleep_sleep函数的用法 您好!您想了解编程语言中`sleep``_sleep`函数的用法。`sleep`函数是常见的标准函数,用于在程序中暂停执行指定的时间(通常以秒或毫秒为单位),常用于模拟延迟、等待操作或控制线程执行节奏。`_sleep`在某些上下文中可能是个不规范写法,例如在Windows平台可能误写为`_sleep`(但标准名称是首字母大写的`Sleep`函数)。在不同编程语言中,sleep函数的实现语法有所差异。我将基于您提供的引用参考,并结合常见编程语言,逐步解释其基本用法、注意事项示例代码。 #### 1. sleep函数的基本概念 - **作用**:sleep函数暂停当前线程或进程的执行一段指定时间,常用于: - 避免资源竞争(如多线程编程)。 - 模拟时间延迟(如等待外部设备响应)。 - 实现定时任务(如周期性执行代码)。 - **单位**:在大多数语言中,参数以秒(秒级)或毫秒(毫秒级)为单位。例如,sleep(1)表示暂停1秒。 - **注意事项**:根据引用[2],sleep函数在Linux中可以被信号中断,这可能导致暂停时间少于指定值[^2]。同时,引用[3]指出,sleep会阻塞当前线程,因此在多线程应用中需谨慎使用以避免性能问题[^3]。 #### 2. 常见编程语言中sleep函数的用法 以下是主流编程语言中sleep函数的标准实现方式(包括类似函数的用法)。`_sleep`通常不规范,我将解释它可能对应的场景。 ##### (1) Python中的sleep函数 在Python中,标准sleep函数位于`time`模块,以秒为单位。 - **语法**:`time.sleep(seconds)` - **参数**:`seconds`可以是整数或浮点数(如0.5表示半秒)。 - **注意事项**:Python的sleep实现不会在当前线程中被中断,而是完全阻塞线程执行。 - **示例代码**: ```python import time # 必须导入time模块 print("开始执行") time.sleep(5) # 暂停5秒 print("5秒后执行") ``` 输出: ``` 开始执行 (等待5秒) 5秒后执行 ``` 引用[1]提到Python中sleep函数用于定时任务实例,但强调它在实践中常用于简单延迟控制[^1]。对于定时任务,推荐使用更专业的scheduler库。 ##### (2) C/C++中的sleep函数 在C/C++中,sleep函数的语法因操作系统而异: - **Unix/Linux系统**:使用标准`sleep`函数,定义在`<unistd.h>`头文件中,单位是秒。 - **语法**:`sleep(unsigned int seconds)` - **示例代码**: ```c #include <unistd.h> // Unix/Linux专用头文件 #include <stdio.h> int main() { printf("开始执行\n"); sleep(3); // 暂停3秒 printf("3秒后执行\n"); return 0; } ``` 引用[2]指出,在Linux中sleep函数可被中断信号打断,导致提前恢复执行[^2]。 - **Windows系统**:Windows平台没有标准的`sleep`函数;标准函数是`Sleep`(首字母大写),定义在`<windows.h>`中,单位是毫秒。 - **语法**:`Sleep(DWORD milliseconds)`。`_sleep`可能是一个错误写法或不规范alias(例如在老旧代码中出现),但推荐使用`Sleep`。 - **注意事项**:`Sleep`也会阻塞当前线程。 - **示例代码**: ```c #include <windows.h> // Windows专用头文件 #include <stdio.h> int main() { printf("开始执行\n"); Sleep(2000); // 暂停2000毫秒(即2秒) printf("2秒后执行\n"); return 0; } ``` 引用[3]提到,在C++中可以使用标准库多线程处理sleep问题以避免阻塞主线程[^3]。对于跨平台应用,建议使用条件变量或std::thread。 ##### (3) Java中的sleep函数 在Java中,sleep函数位于`Thread`类中,单位是毫秒。 - **语法**:`Thread.sleep(long milliseconds)` - **注意事项**:sleep可能抛出`InterruptedException`(表示被中断),需通过try-catch处理。 - **示例代码**: ```java public class SleepExample { public static void main(String[] args) { System.out.println("开始执行"); try { Thread.sleep(3000); // 暂停3000毫秒(3秒) } catch (InterruptedException e) { e.printStackTrace(); // 处理中断异常 } System.out.println("3秒后执行"); } } ``` ##### (4) _sleep的可能含义用法 在您提到的`_sleep`,这可能是个不规范名称: - **在Windows平台**:`_sleep`可能意指`Sleep`函数的别名或用户自定义函数(但Windows API使用`Sleep`)。建议直接使用`Sleep`。 - **在其他语言**:如PHP(基于引用[1]),标准函数是`sleep(int seconds)`,单位秒;在shell脚本中,Linux的sleep命令用于命令行(如`sleep 5`)。 - **最佳实践**:始终使用语言标准名称(如sleep、Thread.sleep等),避免`_sleep`以免混淆。 #### 3. 常见错误最佳实践 - **错误处理**:在多线程环境中,sleep可能引发线程阻塞问题(引用[3])。在Python或Java中,使用异步库(如asyncio)或线程管理工具可优化性能。 - **可中断性**:如引用[2]所述,Linux sleep可被中断信号提前结束,编程时应检查返回值或实现重试机制[^2]。 - **单位混淆**:确保参数单位正确(秒vs毫秒)。例如,Python用秒,Java用毫秒。 - **跨平台兼容性**:在C++中,使用`#ifdef`处理OS差异: ```c #ifdef _WIN32 #include <windows.h> #define SLEEP(ms) Sleep(ms) #else #include <unistd.h> #define SLEEP(s) sleep(s) #endif int main() { SLEEP(1000); // Windows:毫秒;Linux:秒(需调整单位) return 0; } ``` #### 4. 总结 - **sleep函数**:是所有编程语言中实现时间延迟的基础工具,但语法单位因语言/操作系统而异。 - **_sleep的用法**:建议视为不规范写法;在Windows中使用`Sleep`,其他语言使用标准sleep函数。 - **应用场景**:常用于游戏开发(帧率控制)、服务器调度(请求频率限制)或测试脚本(模拟延迟)。 如需更深入讨论特定语言的实际项目案例,请提供更多细节!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值