【问题标题】:Multithreaded printf vs write(2) buffering多线程 printf 与 write(2) 缓冲
【发布时间】:2017-01-18 19:21:16
【问题描述】:

考虑以下在 Solaris 11.3 下运行的代码:

void *run(void *args) {
  int i;
  for (i = 0; i < 100; i++) {
    printf("Line #%d printed by child\n", i + 1);
  }
}

int main() {
  pthread_t tid;
  int ret = pthread_create(&tid, NULL, run, NULL);
  int i;
  for (i = 0; i < 100; i++) {
    printf("Line #%d printed by parent\n", i + 1);
  }
  pthread_exit(NULL);
}

在运行时,父线程总是在新创建的线程之前打印出它的所有行。有人可能会说pthread_create 太慢了,以至于其他线程甚至无法在主线程返回之前开始其工作。但是,将printf 换成write(2) 会产生 线程偶尔轮流打印它们的行的预期输出。

如何解释这种行为?

缓冲当然与它无关,因为终端输出默认是行缓冲的; write 和 printf 都应该是这样。

即使函数使用某种阻塞技术来提供线程安全,究竟是什么可以防止输出加扰?

【问题讨论】:

  • 它是一个缓冲问题。您将write(2) 与fwrite(3) 混淆了。 write(2) 是一个低级函数,没有缓冲。 fwrite(3) 是一个 stdio 函数,默认情况下是缓冲的。你会得到这种效果,因为stdout 在后台线程中被块缓冲,而它在父线程中保持行缓冲。将setbuf(stdout, NULL) 添加到两个线程并进行比较。
  • 另一个线程的输出仍然是到终端,所以不应该像往常一样进行行缓冲吗?
  • “行缓冲”是stdio 的一个方面,而不是“终端”。 write(2) 不使用 stdio,因此没有缓冲。
  • @SatoKatsura:这不是缓冲问题,因为两个线程都在写入同一个缓冲区。对printf 的每次调用都将获取stdout 上的锁,然后写入该行(如果行缓冲将刷新,但如果没有则不会),然后释放锁

标签: solaris c io printf multithreading


【解决方案1】:

这完全是由于速度。如果您在pthread_create 和循环之间调用usleep,并稍微调整值,您可以让它混合一些行。

请注意,由于 stdio 锁定,一旦一个线程未能获得stdout 上的锁定一次或两次,它可能会休眠几毫秒(这将允许另一个线程完成它的循环),所以你不会看到很多混合。如果你想要更多的混合,你也需要在循环中添加一个usleep。

在我的机器上,父循环之前的usleep(50) 和两个循环中的usleep(1) 将导致几乎交替的行。

【讨论】:

  • sched_yield 在我的机器上也可以让线条每次交替,这正是我正在寻找的。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多