【问题标题】:How to make a thread sleep/block for nanoseconds (or at least milliseconds)?如何使线程休眠/阻塞纳秒(或至少毫秒)?
【发布时间】:2011-08-13 19:22:34
【问题描述】:

如何将我的线程(可能是进程)阻塞几纳秒或几毫秒(至少)?

请注意,我不能使用睡眠,因为睡眠的参数总是以秒为单位。

【问题讨论】:

  • 您能解释一下为什么需要如此细粒度的等待吗?
  • 一种实现用户自定义定时器的方法。
  • "nanoseconds or may be milliseconds" nano/milli = 1/1.000.000 -- 你应该确定你想要哪一个。一纳秒大约是 2-3 个 CPU 周期(假设为 2-3 GHz),所以大约是一两条机器指令。
  • 好吧,仅仅调用和返回你的定时器函数就需要两打到 500 纳秒(以防缓存和/或 TLB 未命中)。就此而言,更精细不一定更好,因为更精细并不意味着更精确。操作系统提供的等待/超时功能(请参阅 R... 的回答)尽您所能合理获得。任何比这更好的东西都是愚蠢的。

标签: c linux pthreads sleep


【解决方案1】:

试试usleep()。是的,这不会为您提供纳秒精度,但微秒也可以工作 => 毫秒。

【讨论】:

  • 有什么可以阻止纳秒吗?假设用户给出了等待时间:2.0000045,这将失败。
  • 即使 unix 系统或其他系统支持非秒,它也受到 CPU 速度的限制。让我们看看为什么。考虑 2.5 Ghz 处理器。一秒钟内它可以执行大约 10^8 个处理器命令,这意味着每微秒大约 100 次操作 => 可以在该处理器上处理这种粒度,但每纳秒 0.1 次操作 => 即使支持这种粒度,处理器也可以' t 正确测量时间。
  • usleep 在 POSIX 2003 中被淘汰,并在 2008 年被删除。
【解决方案2】:

在一般的 Linux 操作系统上,精确的纳秒分辨率是不可能的,因为一般 Linux 发行版不是(硬)实时操作系统。如果您真的需要对时间进行精细控制,请考虑使用这样的操作系统。

维基百科在此处列出了一些实时操作系统:http://en.wikipedia.org/wiki/RTOS(请注意,它并没有说明它们是软实时还是硬实时,因此您必须进行一些研究)。

【讨论】:

  • 我们没有使用市场上任何可用的商业 Linux O/S。我们使用自己的发行版。
【解决方案3】:

对 pthread 使用任何 sleep 变体,都无法保证行为。所有线程也可以休眠,因为内核不知道不同的线程。因此需要一个 pthread 库而不是内核可以处理的解决方案。

使用 pthread_cond_timedwait 是一种更安全、更清洁的解决方案...

pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER;

void mywait(int timeInSec)
{
struct timespec timeToWait;
struct timeval now;
int rt;

gettimeofday(&now,NULL);

timeToWait.tv_sec = now.tv_sec + timeInSec;
timeToWait.tv_nsec = now.tv_usec*1000;

pthread_mutex_lock(&fakeMutex);
rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait);
pthread_mutex_unlock(&fakeMutex);
printf("\nDone\n");
}

void* fun(void* arg)
{
printf("\nIn thread\n");
mywait(5);
}

int main()
{
pthread_t thread;
void *ret;

pthread_create(&thread, NULL, fun, NULL);
pthread_join(thread,&ret);
}

对于 pthread_cond_timedwait ,您需要指定从当前时间开始等待多长时间。

现在通过使用 mywait() 函数,只有调用它的线程会休眠,而其他 pthread 则不会。

【讨论】:

  • 内核非常了解线程,从内核的角度来看,它们只是共享内存和其他状态的独立进程。
  • 这个答案有错误的信息。 sleep 及其变体适用于调用线程,而不适用于进程。然而,使用pthread_cond_wait 而不是睡眠函数的想法可能是一个非常好的想法,特别是如果微小睡眠的原因是为了轮询某些条件。
  • @Douglas:如果我没记错的话,内核不知道用户级线程。这就是为什么当一个用户级线程进行阻塞系统调用时,所有用户级线程都会被阻塞。但同样不适用于内核线程。
  • @kingmasher1:你是对的。内核只知道内核线程。它没有关于用户级线程的信息,因此没有问题。
【解决方案4】:

nanosleep 允许您将睡眠的精度指定为纳秒。然而,由于内核/CPU 的限制,您的睡眠的实际分辨率可能要大得多。

【讨论】:

  • 在高端机器上,它确实可以以亚微秒级的分辨率休眠(虽然不会小很多,可能 500-900 ns 是您可以获得的最短休眠时间)。
【解决方案5】:

一种相对可移植的方式是使用select()pselect() 而不使用文件描述符:

void sleep(unsigned long nsec) {
    struct timespec delay = { nsec / 1000000000, nsec % 1000000000 };
    pselect(0, NULL, NULL, NULL, &delay, NULL);
}

【讨论】:

  • 任何带有pselect 的系统几乎肯定也会有nanosleep。我认为它们都是由 POSIX 发明的,并且都是同时添加的。
  • 这是一个不错的选择,但是我们的 CE-Linux 支持 nanosleep,但是,为了清晰和未来的实现,您的代码很有用。谢谢。
  • 这不会按原样编译。
  • @LotoLo 添加了struct,没有注意到它有C标签。
  • 您还可以添加 ppoll() 很长的 pselect() (我假设是 nanosleep())。
【解决方案6】:

nanosleepclock_nanosleep 是您应该使用的函数(后者允许您指定绝对时间而不是相对时间,并使用单调时钟或其他时钟而不仅仅是实时时钟,这可能会向后运行如果操作员重置它)。

但是请注意,就分辨率而言,您很少会比几微秒更好,并且它总是向上取整睡眠的持续时间,而不是向下取整。 (无论如何,舍入通常是不可能的,因为在大多数机器上,进入和退出内核空间需要超过一微秒。)

另外,如果可能的话,我建议使用阻塞等待事件的调用,而不是休眠一小段时间然后轮询。例如,pthread_cond_waitpthread_cond_timedwaitsem_waitsem_timedwaitselectread 等,具体取决于您的线程正在执行的任务以及它如何与其他线程同步和/或与外面的世界。

【讨论】:

  • 请注意,要实现计时器,您可能希望clock_nanosleep 具有绝对时间(预先计算的下一个计时器到期时间)。对于相对时间,除非您采取额外的预防措施来根据测量的时间调整睡眠间隔,否则您会累积误差。
  • 有趣的是,据说我的计算机上的时钟分辨率为 1e-9,尽管在现有架构下等待一纳秒几乎是不可能的。
  • @AlexisWilke:这并不意味着您可以等待一纳秒,只是报告的时间具有低至一纳秒的意义。
【解决方案7】:

在可以访问多个硬件计时器的嵌入式系统上,为您的纳秒或微秒等待创建一个高速时钟。创建一个宏来启用和禁用它,并在定时器中断服务例程中处理您的高分辨率处理。

如果浪费功率和忙于等待不是问题,请执行一些无操作指令 - 但请确认编译器不会优化您的无操作。尝试使用 volatile 类型。

【讨论】:

  • 延迟循环不能在现代高性能 CPU(包括手机)上使用,除了“等待至少几十纳秒”,但可能是 5 倍到10 倍的时间取决于空闲的 CPU 频率与最大 turbo。此外,NOP 不会对延迟产生线性影响,除非在循环中使用足够多的 NOP 来限制前端而不是后端每个时钟采用 1 个分支。另请参阅 How to calculate time for an asm delay loop on x86 linux? 了解 RDTSC 截止日期自旋等待循环。
猜你喜欢
  • 1970-01-01
  • 2011-05-10
  • 2021-11-20
  • 1970-01-01
  • 2017-11-06
  • 1970-01-01
  • 2010-09-10
  • 2010-09-27
  • 2015-11-14
相关资源
最近更新 更多