我们的代码用C 11(VS2012 / Win 7-64bit)编写. C库提供了我们使用的sleep_for函数.我们观察到C sleep_for有时显示出大的过冲.换句话说,我们要求睡觉15分钟,但睡眠结果是例如100毫秒当系统负载很高时,我们会看到这一点.

我的第一反应是:“当然,如果系统上有很多负载而其他线程正在使用cpu,那么睡眠”需要更长的时间“.
然而,“有趣”的是,如果我们用Windows API“Sleep”调用替换sleep_for,那么我们就不会看到这种行为.我还看到了水下的sleep_for函数调用了Window API Sleep方法.

sleep_for的文档指出:

The function blocks the calling thread for at least the time that’s specified by Rel_time. This function does not throw any exceptions.

从技术上讲,功能正在发挥作用.但是我们没想到看到C sleep_for和常规Sleep(Ex)功能之间存在差异.

有人可以解释这种行为吗?

如果使用sleep_for vs SleepEx,还会执行相当多的额外代码.

例如,调用SleepEx(15)在调试模式下生成以下程序集(Visual Studio 2015):

; 9    :    SleepEx(15,false);

    mov esi,esp
    push    0
    push    15                  ; 0000000fH
    call    DWORD PTR __imp__SleepEx@8
    cmp esi,esp
    call    __RTC_CheckEsp

相比之下,这段代码

const std::chrono::milliseconds duration(15);
std::this_thread::sleep_for(duration);

生成以下内容:

; 9    :    std::this_thread::sleep_for(std::chrono::milliseconds(15));

    mov DWORD PTR $T1[ebp],15          ; 0000000fH
    lea eax,DWORD PTR $T1[ebp]
    push    eax
    lea ecx,DWORD PTR $T2[ebp]
    call    duration
    push    eax
    call    sleep_for
    add esp,4

这呼吁:

duration PROC ; std::chrono::duration<__int64,std::ratio<1,1000> >::duration<__int64,1000> ><int,void>,COMDAT
; _this$= ecx

; 113  :        {   // construct from representation

    push    ebp
    mov ebp,esp
    sub esp,204                ; 000000ccH
    push    ebx
    push    esi
    push    edi
    push    ecx
    lea edi,DWORD PTR [ebp-204]
    mov ecx,51                 ; 00000033H
    mov eax,-858993460             ; ccccccccH
    rep stosd
    pop ecx
    mov DWORD PTR _this$[ebp],ecx

; 112  :            : _MyRep(static_cast<_Rep>(_Val))

    mov eax,DWORD PTR __Val$[ebp]
    mov eax,DWORD PTR [eax]
    cdq
    mov ecx,DWORD PTR _this$[ebp]
    mov DWORD PTR [ecx],eax
    mov DWORD PTR [ecx+4],edx

; 114  :        }

    mov eax,DWORD PTR _this$[ebp]
    pop edi
    pop esi
    pop ebx
    mov esp,ebp
    pop ebp
    ret 4
duration ENDP

并打电话给

sleep_for PROC ; std::this_thread::sleep_for<__int64,1000> >,COMDAT

    ; 151  :    {   // sleep for duration

        push    ebp
        mov ebp,esp
        sub esp,268                ; 0000010cH
        push    ebx
        push    esi
        push    edi
        lea edi,DWORD PTR [ebp-268]
        mov ecx,67                 ; 00000043H
        mov eax,-858993460             ; ccccccccH
        rep stosd
        mov eax,DWORD PTR ___security_cookie
        xor eax,ebp
        mov DWORD PTR __$ArrayPad$[ebp],eax

    ; 152  :    stdext::threads::xtime _Tgt = _To_xtime(_Rel_time);

        mov eax,DWORD PTR __Rel_time$[ebp]
        push    eax
        lea ecx,DWORD PTR $T1[ebp]
        push    ecx
        call    to_xtime
        add esp,8
        mov edx,DWORD PTR [eax]
        mov DWORD PTR $T2[ebp],edx
        mov ecx,DWORD PTR [eax+4]
        mov DWORD PTR $T2[ebp+4],ecx
        mov edx,DWORD PTR [eax+8]
        mov DWORD PTR $T2[ebp+8],edx
        mov eax,DWORD PTR [eax+12]
        mov DWORD PTR $T2[ebp+12],eax
        mov ecx,DWORD PTR $T2[ebp]
        mov DWORD PTR __Tgt$[ebp],DWORD PTR $T2[ebp+4]
        mov DWORD PTR __Tgt$[ebp+4],DWORD PTR $T2[ebp+8]
        mov DWORD PTR __Tgt$[ebp+8],DWORD PTR $T2[ebp+12]
        mov DWORD PTR __Tgt$[ebp+12],ecx

    ; 153  :    sleep_until(&_Tgt);

        lea eax,DWORD PTR __Tgt$[ebp]
        push    eax
        call    sleep_until
        add esp,4

    ; 154  :    }

        push    edx
        mov ecx,ebp
        push    eax
        lea edx,DWORD PTR $LN5@sleep_for
        call    @_RTC_CheckStackVars@8
        pop eax
        pop edx
        pop edi
        pop esi
        pop ebx
        mov ecx,DWORD PTR __$ArrayPad$[ebp]
        xor ecx,ebp
        call    @__security_check_cookie@4
        add esp,268                ; 0000010cH
        cmp ebp,esp
        call    __RTC_CheckEsp
        mov esp,ebp
        pop ebp
        ret 0
        npad    3
    $LN5@sleep_for:
        DD  1
        DD  $LN4@sleep_for
    $LN4@sleep_for:
        DD  -24                 ; ffffffe8H
        DD  16                  ; 00000010H
        DD  $LN3@sleep_for
    $LN3@sleep_for:
        DB  95                  ; 0000005fH
        DB  84                  ; 00000054H
        DB  103                 ; 00000067H
        DB  116                 ; 00000074H
        DB  0
    sleep_for ENDP

一些转换发生:

to_xtime PROC ; std::_To_xtime<__int64,COMDAT

; 758  :    {   // convert duration to xtime

    push    ebp
    mov ebp,348                ; 0000015cH
    push    ebx
    push    esi
    push    edi
    lea edi,DWORD PTR [ebp-348]
    mov ecx,87                 ; 00000057H
    mov eax,-858993460             ; ccccccccH
    rep stosd
    mov eax,DWORD PTR ___security_cookie
    xor eax,ebp
    mov DWORD PTR __$ArrayPad$[ebp],eax

; 759  :    xtime _Xt;
; 760  :    if (_Rel_time <= chrono::duration<_Rep,_Period>::zero())

    lea eax,DWORD PTR $T7[ebp]
    push    eax
    call    duration_zero ; std::chrono::duration<__int64,1000> >::zero
    add esp,4
    push    eax
    mov ecx,DWORD PTR __Rel_time$[ebp]
    push    ecx
    call    chronos_operator ; std::chrono::operator<=<__int64,1000>,__int64,1000> >
    add esp,8
    movzx   edx,al
    test    edx,edx
    je  SHORT $LN2@To_xtime

; 761  :        {   // negative or zero relative time,return zero
; 762  :        _Xt.sec = 0;

    xorps   xmm0,xmm0
    movlpd  QWORD PTR __Xt$[ebp],xmm0

; 763  :        _Xt.nsec = 0;

    mov DWORD PTR __Xt$[ebp+8],0

; 764  :        }
; 765  :    else

    jmp $LN3@To_xtime
$LN2@To_xtime:

; 766  :        {   // positive relative time,convert
; 767  :        chrono::nanoseconds _T0 =
; 768  :            chrono::system_clock::Now().time_since_epoch();

    lea eax,DWORD PTR $T5[ebp]
    push    eax
    lea ecx,DWORD PTR $T6[ebp]
    push    ecx
    call    system_clock_Now ; std::chrono::system_clock::Now
    add esp,4
    mov ecx,eax
    call    time_since_ephoch ; std::chrono::time_point<std::chrono::system_clock,std::chrono::duration<__int64,10000000> > >::time_since_epoch
    push    eax
    lea ecx,DWORD PTR __T0$8[ebp]
    call    duration ; std::chrono::duration<__int64,1000000000> >::duration<__int64,1000000000> ><__int64,10000000>,void>

; 769  :        _T0 += _Rel_time;

    mov eax,DWORD PTR __Rel_time$[ebp]
    push    eax
    lea ecx,DWORD PTR $T4[ebp]
    call    duration_ratio ; std::chrono::duration<__int64,void>
    lea ecx,DWORD PTR $T4[ebp]
    push    ecx
    lea ecx,DWORD PTR __T0$8[ebp]
    call    duration_ratio ; std::chrono::duration<__int64,1000000000> >::operator+=

; 770  :        _Xt.sec = chrono::duration_cast<chrono::seconds>(_T0).count();

    lea eax,DWORD PTR __T0$8[ebp]
    push    eax
    lea ecx,DWORD PTR $T3[ebp]
    push    ecx
    call    duration_cast ; std::chrono::duration_cast<std::chrono::duration<__int64,1> >,1000000000> >
    add esp,8
    mov ecx,eax
    call    duration_count ; std::chrono::duration<__int64,1> >::count
    mov DWORD PTR __Xt$[ebp],eax
    mov DWORD PTR __Xt$[ebp+4],edx

; 771  :        _T0 -= chrono::seconds(_Xt.sec);

    lea eax,DWORD PTR __Xt$[ebp]
    push    eax
    lea ecx,DWORD PTR $T1[ebp]
    call    duration_ratio ; std::chrono::duration<__int64,1> >::duration<__int64,1> ><__int64,void>
    push    eax
    lea ecx,DWORD PTR $T2[ebp]
    call    duration_ratio ; std::chrono::duration<__int64,1>,DWORD PTR $T2[ebp]
    push    ecx
    lea ecx,1000000000> >::operator-=

; 772  :        _Xt.nsec = (long)_T0.count();

    lea ecx,1000000000> >::count
    mov DWORD PTR __Xt$[ebp+8],eax
$LN3@To_xtime:

; 773  :        }
; 774  :    return (_Xt);

    mov eax,DWORD PTR $T9[ebp]
    mov ecx,DWORD PTR __Xt$[ebp]
    mov DWORD PTR [eax],ecx
    mov edx,DWORD PTR __Xt$[ebp+4]
    mov DWORD PTR [eax+4],edx
    mov ecx,DWORD PTR __Xt$[ebp+8]
    mov DWORD PTR [eax+8],DWORD PTR __Xt$[ebp+12]
    mov DWORD PTR [eax+12],edx
    mov eax,DWORD PTR $T9[ebp]

; 775  :    }

    push    edx
    mov ecx,ebp
    push    eax
    lea edx,DWORD PTR $LN8@To_xtime
    call    @_RTC_CheckStackVars@8
    pop eax
    pop edx
    pop edi
    pop esi
    pop ebx
    mov ecx,DWORD PTR __$ArrayPad$[ebp]
    xor ecx,ebp
    call    @__security_check_cookie@4
    add esp,348                ; 0000015cH
    cmp ebp,esp
    call    __RTC_CheckEsp
    mov esp,ebp
    pop ebp
    ret 0
$LN8@To_xtime:
    DD  2
    DD  $LN7@To_xtime
$LN7@To_xtime:
    DD  -24                 ; ffffffe8H
    DD  16                  ; 00000010H
    DD  $LN5@To_xtime
    DD  -40                 ; ffffffd8H
    DD  8
    DD  $LN6@To_xtime
$LN6@To_xtime:
    DB  95                  ; 0000005fH
    DB  84                  ; 00000054H
    DB  48                  ; 00000030H
    DB  0
$LN5@To_xtime:
    DB  95                  ; 0000005fH
    DB  88                  ; 00000058H
    DB  116                 ; 00000074H
    DB  0
to_xtime ENDP

最终调用导入的函数,与SleepEx使用的函数相同.

sleep_until PROC    ; std::this_thread::sleep_until,COMDAT

; 131  :    {   // sleep until _Abs_time

    push    ebp
    mov ebp,192                ; 000000c0H
    push    ebx
    push    esi
    push    edi
    lea edi,DWORD PTR [ebp-192]
    mov ecx,48                 ; 00000030H
    mov eax,-858993460             ; ccccccccH
    rep stosd

; 132  :    _Thrd_sleep(_Abs_time);

    mov esi,esp
    mov eax,DWORD PTR __Abs_time$[ebp]
    push    eax
    call    DWORD PTR __imp___Thrd_sleep
    add esp,4
    cmp esi,esp
    call    __RTC_CheckEsp

; 133  :    }

    pop edi
    pop esi
    pop ebx
    add esp,192                ; 000000c0H
    cmp ebp,ebp
    pop ebp
    ret 0
sleep_until ENDP

您还应该注意,即使SleepEx可能无法根据MSDN文档https://msdn.microsoft.com/en-us/library/windows/desktop/ms686307(v=vs.85).aspx提供100%的确切结果

此函数使线程放弃其时间片的剩余部分,并在基于dwMilliseconds值的时间间隔内变得不可用.系统时钟以恒定速率“滴答”.如果dwMilliseconds小于系统时钟的分辨率,则线程可能会睡眠时间少于指定的时间长度.如果dwMilliseconds大于一个tick但小于2,则等待可以是一到两个滴答之间的任何位置,依此类推.要提高休眠间隔的准确性,请调用timeGetDevCaps函数以确定支持的最小计时器分辨率,并调用timeBeginPeriod函数将计时器分辨率设置为最小值.调用timeBeginPeriod时要小心,因为频繁的调用会显着影响系统时钟,系统功耗和调度程序.如果你调用timeBeginPeriod,在应用程序的早期调用它一次,并确保在应用程序的最后调用timeEndPeriod函数.

Windows 7:过冲C std :: this_thread :: sleep_for的更多相关文章

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. js中‘!.’是什么意思

  3. InnoDB 和 MyISAM 引擎恢复数据库,使用 .frm、.ibd文件恢复数据库

  4. Error: Cannot find module ‘node:util‘问题解决

    控制台 安装 Vue-Cli 最后一步出现 Error: Cannot find module 'node:util' 问题解决方案1.问题C:\Windows\System32>cnpm install -g @vue/cli@4.0.3internal/modules/cjs/loader.js:638 throw err; &nbs

  5. yarn的安装和使用(全网最详细)

    一、yarn的简介:Yarn是facebook发布的一款取代npm的包管理工具。二、yarn的特点:速度超快。Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。超级安全。在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。超级可靠。使用详细、简洁的锁文件格式和明确的安装算法,Yarn 能够保证在不同系统上无差异的工作。三、y

  6. 前端环境 本机可切换node多版本 问题源头是node使用的高版本

    前言投降投降 重头再来 重装环境 也就分分钟的事 偏要折腾 这下好了1天了 还没折腾出来问题的源头是node 使用的高版本 方案那就用 本机可切换多版本最终问题是因为nodejs的版本太高,导致的node-sass不兼容问题,我的node是v16.14.0的版本,项目中用了"node-sass": "^4.7.2"版本,无法匹配当前的node版本根据文章的提

  7. 宝塔Linux的FTP连接不上的解决方法

    宝塔Linux的FTP连接不上的解决方法常见的几个可能,建议先排查。1.注意内网IP和外网IP2.检查ftp服务是否启动 (面板首页即可看到)3.检查防火墙20端口 ftp 21端口及被动端口39000 - 40000是否放行 (如是腾讯云/阿里云等还需检查安全组)4.是否主动/被动模式都不能连接5.新建一个用户看是否能连接6.修改ftp配置文件 将ForcePassiveIP前面的#去掉 将19

  8. 扩展element-ui el-upload组件,实现复制粘贴上传图片文件,带图片预览功能

  9. 微信小程序canvas实现水平、垂直居中效果

    这篇文章主要介绍了小程序中canvas实现水平、垂直居中效果,本文图文实例代码相结合给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

  10. 使用HTML5做的导航条详细步骤

    这篇文章主要介绍了用HTML5做的导航条详细步骤,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

随机推荐

  1. static – 在页面之间共享数据的最佳实践

    我想知道在UWP的页面之间发送像’selectedItem’等变量的最佳做法是什么?创建一个每个页面都知道的静态全局变量类是一个好主意吗?

  2. .net – 为Windows窗体控件提供百分比宽度/高度

    WindowsForm开发的新手,但在Web开发方面经验丰富.有没有办法为Windows窗体控件指定百分比宽度/高度,以便在用户调整窗口大小时扩展/缩小?当窗口调整大小时,可以编写代码来改变控件的宽度/高度,但我希望有更好的方法,比如在HTML/CSS中.在那儿?

  3. 使用Windows Azure查询表存储数据

    我需要使用特定帐户吗?>将应用程序部署到Azure服务后,如何查询数据?GoogleAppEngine有一个数据查看器/查询工具,Azure有类似的东西吗?>您可以看到的sqlExpressintance仅在开发结构中,并且一旦您表示没有等效,所以请小心使用它.>您可以尝试使用Linqpad查询表格.看看JamieThomson的thispost.

  4. windows – SetupDiGetClassDevs是否与文档中的设备实例ID一起使用?

    有没有更好的方法可以使用DBT_DEVICEARRIVAL事件中的数据获取设备的更多信息?您似乎必须指定DIGCF_ALLCLASSES标志以查找与给定设备实例ID匹配的所有类,或者指定ClassGuid并使用DIGCF_DEFAULT标志.这对我有用:带输出:

  5. Windows Live ID是OpenID提供商吗?

    不,WindowsLiveID不是OpenID提供商.他们使用专有协议.自从他们的“测试版”期结束以来,他们从未宣布计划继续它.

  6. 如果我在代码中进行了更改,是否需要重新安装Windows服务?

    我写了一个Windows服务并安装它.现在我对代码进行了一些更改并重新构建了解决方案.我还应该重新安装服务吗?不,只需停止它,替换文件,然后重新启动它.

  7. 带有双引号的字符串回显使用Windows批处理输出文件

    我正在尝试使用Windows批处理文件重写配置文件.我循环遍历文件的行并查找我想要用指定的新行替换的行.我有一个’函数’将行写入文件问题是%Text%是一个嵌入双引号的字符串.然后失败了.可能还有其他角色也会导致失败.如何才能使用配置文件中的所有文本?尝试将所有“在文本中替换为^”.^是转义字符,因此“将被视为常规字符你可以尝试以下方法:其他可能导致错误的字符是:

  8. .net – 将控制台应用程序转换为服务?

    我正在寻找不同的优势/劣势,将我们长期使用的控制台应用程序转换为Windows服务.我们为ActiveMQ使用了一个叫做java服务包装器的东西,我相信人们告诉我你可以用它包装任何东西.这并不是说你应该用它包装任何东西;我们遇到了这个问题.控制台应用程序是一个.NET控制台应用程序,默认情况下会将大量信息记录到控制台,尽管这是可配置的.任何推荐?我们应该在VisualStudio中将其重建为服务吗?我使用“-install”/“-uninstall”开关执行此操作.例如,seehere.

  9. windows – 捕获外部程序的STDOUT和STDERR *同时*它正在执行(Ruby)

    哦,我在Windows上:-(实际上,它比我想象的要简单,这看起来很完美:…是的,它适用于Windows!

  10. windows – 当我试图批量打印变量时,为什么我得到“Echo is on”

    我想要执行一个简单的批处理文件脚本:当我在XP中运行时,它给了我预期的输出,但是当我在Vista或Windows7中运行它时,我在尝试打印值时得到“EchoisOn”.以下是程序的输出:摆脱集合表达式中的空格.等号(=)的两侧可以并且应该没有空格BTW:我通常在@echo关闭的情况下启动所有批处理文件,并以@echo结束它们,所以我可以避免将代码与批处理文件的输出混合.它只是使您的批处理文件输出更好,更清洁.

返回
顶部