Windows 7:过冲C std :: this_thread :: sleep_for

我们的代码用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函数.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读2.2k次,点赞6次,收藏20次。在我们平时办公工作中,很多时候我们经常会使用到虚拟机来进行环境的测试,我们平时在虚拟机上接触的最多的莫过于Linux和Winwdos。不过虚拟机环境和物理机环境是无法直接传输的,那么有的时候呢,同学们又想要在两者之间相互传输文件,可能就会使用QQ邮箱等形式来传输,这样的效率又慢而且繁琐,今天我就为大家带来一种非常便捷的传输方式。通过XFTP工具来进行文件传输。_xftp连接windows
文章浏览阅读1k次。解决 Windows make command not found 和 安装 GCC 环境_windows下载gcc
文章浏览阅读3.2k次,点赞2次,收藏6次。2、鼠标依次点击“计算机配置“ - ”管理模板“ - ”网络“ - ”Lanman工作站”,点击右侧的“启用不安全的来宾登录”策略。Windows访问samba共享时,提示“你不能访问此共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问”1、键盘按下window+R键,输入gpedit.msc,启动本地组策略编辑器。首先在终端中输入sudo ufw status查看当前防火墙状态。默认状态是“未配置”,修改为“已启用”。示例:创建一个narada的目录在/home下。1.更新apt储存库列表。_ubuntu samba 目标文件夹访问被拒绝
文章浏览阅读1.3w次。蓝光版属于高清版的一种。BD英文全名是Blu-ray Disc,一种高清的电影版本,这种电影十分清晰但是数据量巨大,占数十G甚至上百G的容量,只有蓝光光碟才能装得下,所以这种高清电影被称为BD版。一般的高清电影多半是从蓝光电影、国外的高清电视频道上压制而来的,可以通过网络下载,多数都经过二次压缩,画质要逊于原视频,不过压缩后的容量从蓝光的25G-50G会减少成4G-8G等(15G-20G不等)。众所周知,视频有两种常见的清晰度,BD和HD,在看电影的时候最常出现这两个标志,那么BD和HD具体指的是什么呢?_bd hd
文章浏览阅读974次,点赞7次,收藏8次。提供了更强大的功能,因为它允许直接访问当前元素,而不需要类型转换。接口,它可以提供一个迭代器,用于按顺序访问集合中的元素。接口是只读的,它只能支持前向迭代,不能修改集合中的元素。类型的集合实例,并向其中添加了几个元素。接口,可以创建一个能够迭代访问泛型集合中元素的迭代器。接口,我们可以在 C# 中实现可迭代的集合,并使用。循环和迭代器手动遍历集合,并输出每个元素的值。接口表示一个可枚举的集合,它定义了一个方法。属性,用于获取集合中当前位置的元素。存储集合中的元素,并实现了。的泛型集合类,它实现了。
文章浏览阅读1.4w次,点赞5次,收藏22次。如果使用iterator的remove方法则会正常,因为iterator的remove方法会在内部调用List的remove方法,但是会修改excepedModCount的值,因此会正常运行。因为遍历过程中进行remove 操作时,该位置后面的元素会挤到前面来,这时候会发生一种情况就是原来元素的位置会被他后面的元素取代,而该位置已经遍历过了,所以该元素不会背遍历。当我们倒序遍历元素的时候,无论删除元素之后的元素怎么移动,之前的元素对应的索引(index)是不会发生变化的,所以在删除元素的时候不会发生问题。_list删除某个元素
文章浏览阅读2.9w次,点赞45次,收藏192次。Windows下配置Visual Studio _vs2022环境变量配置
文章浏览阅读7w次,点赞162次,收藏778次。pip 是Python包管理工具,提供了对 Python 包的查找、下载、安装、卸载的功能,目前Python 3.4 和 2.7 及以上版本都有配套安装,一般pip的位置在...pythonScripts文件夹里面,而在其他版本需要自行下载。_python pip install安装
文章浏览阅读5.8k次,点赞2次,收藏12次。①此电脑右击----->选择属性----->高级系统设置----->环境变量----->path----->编辑----->新建。第一个选项意思就是将安装路径填入到系统环境变量中,这里勾选,后面使用可能会出现问题,建议不要勾选,安装好之后手动添加环境变量。注意:如果提示conda不是内部或外部命令,原因是Anaconda的环境变量没配置好。如果不想立即打开anaconda,不勾选直接finish就好。②输入 conda --version ,查看conda环境。②直接按win键,搜索“环境变量”_windows安装anaconda
文章浏览阅读5.1k次,点赞8次,收藏55次。Windows 系统从零配置 Python 环境,安装CUDA、CUDNN、PyTorch 详细教程_windows cuda cudnn配置
文章浏览阅读1.5w次,点赞54次,收藏68次。macOS系统自带有VNC远程桌面,我们可以在控制端上安装配置VNC客户端,以此来实现远程控制macOS。但通常需要在不同网络下进行远程控制,为此,我们可以在macOS被控端上使用cpolar做内网穿透,映射VNC默认端口5900,通过所生成的公网地址,来实现在公网环境下远程控制VNC。_vnc mac
文章浏览阅读2.4k次,点赞5次,收藏11次。进入后根据自己的电脑系统下载,这是python 3.10版本下载地址,如果想要下载其它版本可进入此链接(下载完成后点击进行安装点击下一步,到这一步时,可以选择将Anaconda添加我的PATH环境变量中,这样就不用自己手动配置和环境变量。安装完成后,打开终端,输出 python 命令可查看是否安装成功。如果显示自己刚才安装的版本号说明安装成功。查看conda版本命令:conda info。_paddlespeech下载
文章浏览阅读3.3k次。所以如果要删除之前新增的课程编译原理,只需输入命令del Course:8:Cname,同时还应该把本课程的学分删除del Course:8:Ccredit,如下图所示;Redis并没有修改数据的命令,所以如果在Redis中要修改一条数据,只能在使用set命令时,使用同样的键值,然后用新的value值来覆盖旧的数据。先调用get命令,输出原先的值,然后set新的值,最后再get得到新值,所以修改成功。输入命令后没有报错,表示成功了,刷新windows的服务,多了一个redis服务。_redis windows服务
文章浏览阅读2.1w次,点赞9次,收藏56次。​​接着在【工作负荷】中,选择【使用C++桌面开发】 ,右边【安装详细信息】去除其它可选项,只勾选【MSVCv142 】和 【Windows 10 SDK】,按图示修改,然后右下角点击安装,之后会有提示让你重启电脑。重启电脑之后,再进行pip安装。报错原因是pip所安装的包需要使用C++编译后才能够正常安装,但是当前安装环境中缺少完整的C++编译环境,因此安装失败。3.安装Microsoft Visual C++ Build Tool离线安装包(1个多G),CSDN资源很多,需要积分下载,_error: microsoft visual c++ 14.0 or greater is required. get it with "micros
文章浏览阅读1.1w次,点赞3次,收藏7次。Step 3: 在右侧窗口中找到名称为“LongPathsEnabled”的“DWORD (32 位) 值”条目,并双击它。通过注册表方法或组策略方法启用长路径支持后,您将能够在 Windows 中使用长路径,并能够访问和处理长路径下的文件和文件夹。Step 2: 依次选择“计算机配置” > “管理模板” > “系统” > “文件资源管理器”。Step 3: 找到“启用 Win32 长路径”设置,双击它。Step 4: 选择“已启用”选项按钮,然后选择“应用”按钮。_windows长路径支持
文章浏览阅读2.5k次,点赞81次,收藏86次。
文章浏览阅读1.3k次,点赞65次,收藏50次。顺序表,链表,栈,队列,ArrayList,LinkedList,Stack,Queue
文章浏览阅读2.3k次,点赞2次,收藏2次。AnyTXTSearcher是一款能够帮助我们对文档以及文本内容进行快速搜索和管理的工具,通过该软件能够搜索各种Office文档,文本文件,代码,PDF文档等,顶级的全文搜索引擎1秒钟之内即可完成搜索。_anytxt searcher
文章浏览阅读8.8k次,点赞73次,收藏70次。有时,在删除/移动/重命名文件夹/文件时,会遇到如下警告,即使将打开的程序关闭了,后台也可能会有没关干净的相关进程。_解除占用
文章浏览阅读4.3w次,点赞91次,收藏102次。JDK(Java Development Kit)是Java开发工具包的缩写,包含了Java编译器、Java虚拟机、Java类库等众多组件,是Java开发的基石,提供了编写、编译和运行Java程序所必需的工具。同时,为了让系统能够正确识别Java环境,在开始使用JDK进行Java开发之前,需要先把JDK安装到本地计算机,并配置好相应的环境变量。本文将介绍JDK安装与环境变量配置的方法。_windows安装jdk并配置环境变量