何时以及为何需要sleep()?

时间:2009-03-18 16:16:59

标签: c++ c operating-system

cout<<"abcd";

sleep(100);

cout<<'\b';

如果我想打印出字符串然后取回一个字符, 为什么需要sleep()?

但是当在C中使用printf时,似乎没有必要,为什么?

char* a = "12345";
char* b = "67890";
threadA(){cout<<a;}
threadB(){cout<<b;}

beginthread (threadA);
sleep(100);
beginthread (threadB);

在上面的第二个伪代码中,使用sleep()是对的吗?

12 个答案:

答案 0 :(得分:25)

计算明天的日期:

void get_tomorrow_date( struct timeval *date )
{
    sleep( 86400 ); // 60 * 60 * 24
    gettimeofday( date, 0 );
}

答案 1 :(得分:9)

您需要了解两个微妙的问题:

  
      
  • 多线程
  •   
  • I / O和缓冲
  •   

我会试着给你一些想法:

  

多线程和睡眠

在线程环境中拥有sleep是有道理的。 sleep调用使您等待,从而为初始线程提供一些范围以完成其处理,即在另一个线程插入退格字符之前将字符串abcd写出到标准输出。如果您没有等待第一个线程完成其处理,那么您首先编写退格字符,然后编写字符串abcd,并且不会发现任何差异。

  

缓冲I / O

I / O通常发生在缓冲,非缓冲和半缓冲状态。这可能会影响您必须等待输出显示在控制台上的时间长度。

你的cout实现可能正在使用缓冲模型。尝试在endl语句末尾添加换行符或cout以打印新行并立即刷新,或使用cout << "abcd" << flush;进行刷新而不打印新行。

答案 2 :(得分:5)

在没有睡眠的第二种情况下,第二个线程可以在第一个线程之前开始工作,从而导致输出“6789012345”。

然而,“睡眠”并不是处理线程之间同步的方法。您通常会使用threadA()中的threadB()中的信号量或类似信号,{{1}}在开始工作之前必须等待。

答案 3 :(得分:2)

调用sleep会使代码工作的原因是因为您正在使用它来将两个输出流操作的潜在并行执行转换为单个顺序操作。对sleep()的调用将允许调度程序从主执行线程切换并执行线程A.

如果你没有把sleep()放进去,那么线程执行的顺序就不能保证,并且线程B很可能在线程A有机会执行之前开始执行/打印。

答案 4 :(得分:2)

我认为您需要了解sleep的一般情况,并了解其可能存在的原因。

sleep听起来像是这样。它指示操作系统将请求任务(任务是执行的线程)置于休眠状态,方法是将其从当前正在运行的进程列表中删除并将其置于某种等待队列中。

请注意,无论您喜欢与否,操作系统都会让您入睡。一个例子是任何形式的阻塞I / O,比如从磁盘读取文件。操作系统执行此操作,以便在您等待数据时,其他任务可能会引起CPU的注意。

人们会自愿使用sleep用于操作系统的类似目的。例如,如果您有多个线程并且它们正在等待某些计算的完成,那么您可能希望自愿放弃CPU以便计算完成。您也可以自愿放弃CPU,以便其他线程有机会运行。例如,如果你有一个高度CPU限制的紧密循环,你现在想要sleep给其他线程一个机会运行。

你正在做的事情是为了将某些东西刷新到stdout而睡觉,以便其他一些线程在你之前不会写入stdout。但是,这并不能保证有效。它可能会偶然发挥作用,但肯定不是你想要通过设计做的事情。您要么显式刷新缓冲区而根本不要睡眠,要么使用某种形式的同步。

至于为什么printf没有表现出这些问题......好吧,这是一个废话。 printfcout都使用某种形式的缓冲输出,但每种输出的实现可能不同。

总之,最好记住以下内容:

  • 如果要同步,请使用同步原语。
  • 当你想让别人有机会跑步时,请睡觉。
  • 操作系统更擅长判断I / O操作是否阻塞。

答案 5 :(得分:1)

第一个示例中的睡眠只是在您看到“退格”动作之前打印消息。在第二个例子中,睡眠“可以”帮助。但这很奇怪。在某些更复杂的情况下,您将无法将控制台输出与睡眠同步。

答案 6 :(得分:1)

如果你在看到打印“abcd”时遇到问题,那是因为你没有给cout一个终结字符来刷新缓冲区。

如果你把

cout << "abcd" << endl;

你将能够看到角色,然后它会发出哔哔声。没必要睡觉。

答案 7 :(得分:1)

while( true )
{
   msgStack.Lock();
   process( msgStack.pop_msg());   
   msgStack.Unlock();
   sleep(0);
}

答案 8 :(得分:1)

在启动两个线程的代码中:

beginthread (threadA);
sleep(100);
beginthread (threadB);

sleep()等待100毫秒然后继续。程序员可能这样做是为了让threadA有机会在启动threadB之前启动。如果在启动threadB之前必须等待threadA初始化并运行,那么您需要一种等待threadA启动的机制,但这是错误的方法。

100是一个神奇的cookie,任意选择,可能伴随着一个想法,“它应该永远不会让线程超过100毫秒启动。”这样的假设是错误的,因为你无法知道threadA启动需要多长时间。如果机器繁忙或者如果线程A的实现发生变化,则线程启动时可能需要超过100毫秒,运行其启动代码,并进入主循环(如果是那种线程)。

而不是在任意时间内休眠,threadA需要告诉主线程何时启动&amp;运行。这样做的一种常见方式是通过发出事件信号。

说明如何执行此操作的示例代码:

#include "stdafx.h"
#include <windows.h>
#include <process.h>


struct ThreadParam
{
    HANDLE running_;
    HANDLE die_;
};

DWORD WINAPI threadA(void* pv)
{
    ThreadParam* param = reinterpret_cast<ThreadParam*>(pv);
    if( !param )
        return 1;

    // do some initialization
    //  :   :

    SetEvent(param->running_);
    WaitForSingleObject(param->die_, INFINITE);
    return 0;
}

DWORD WINAPI threadB(void* pv)
{
    ThreadParam* param = reinterpret_cast<ThreadParam*>(pv);
    if( !param )
        return 1;

    // do some initialization
    //  :   :

    SetEvent(param->running_);
    WaitForSingleObject(param->die_, INFINITE);
    return 0;
}

int main(int argc, char** argv) 
{
    ThreadParam 
        paramA = {CreateEvent(0, 1, 0, 0), CreateEvent(0, 1, 0, 0) },
        paramB = {CreateEvent(0, 1, 0, 0), CreateEvent(0, 1, 0, 0) };

    DWORD idA = 0, idB = 0;
    // start thread A, wait for it to initialize
    HANDLE a = CreateThread(0, 0, threadA, (void*)&paramA, 0, &idA);
    WaitForSingleObject(paramA.running_, INFINITE);
    // start thread B, wait for it to initi
    HANDLE b = CreateThread(0, 0, threadB, (void*)&paramB, 0, &idB);
    WaitForSingleObject(paramB.running_, INFINITE);
    // tell both threads to die
    SetEvent(paramA.die_);
    SetEvent(paramB.die_);
    CloseHandle(a);
    CloseHandle(b);
    return 0;
}

答案 9 :(得分:0)

不需要 - 如果省略它会得到什么输出?

答案 10 :(得分:0)

睡眠的唯一功能是在调用线程上暂停执行指定的毫秒数。它绝不会影响您可能进行的任何打印的结果。

答案 11 :(得分:0)

睡眠可以用来避免某个线程/进程(是的,我知道它们是不同的东西)占用处理器。

另一方面,printf是线程安全的。 Cout不是。这可以解释他们行为的差异。