10

我最近开始根据 POSIX 1003.1c 学习 pthreads 的奇妙之处。

PThreads 可能看起来很复杂,但它们基本上是我们在类中用来创建并行行为的简单线程:https ://computing.llnl.gov/tutorials/pthreads/

在我还在学习的时候,我的老师给了我们一个 C 代码来玩弄:

/* Creates two threads, one printing 10000 "a"s, the other printing
   10000 "b"s.
   Illustrates: thread creation, thread joining. */

#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include "pthread.h"

void * process(void * arg)
{
  int i;
  fprintf(stderr, "Starting process %s\n", (char *) arg);
  for (i = 0; i < 100; i++) {
       write(1, (char *) arg, 1);
//      fprintf(stdout, (char *) arg, 1);
  }
  return NULL;
}

int main()
{
  int retcode;
  pthread_t th_a, th_b;
  void * retval;

  retcode = pthread_create(&th_a, NULL, process, "a");
  if (retcode != 0) fprintf(stderr, "create a failed %d\n", retcode);

  retcode = pthread_create(&th_b, NULL, process, "b");
  if (retcode != 0) fprintf(stderr, "create b failed %d\n", retcode);

  retcode = pthread_join(th_a, &retval);
  if (retcode != 0) fprintf(stderr, "join a failed %d\n", retcode);

  retcode = pthread_join(th_b, &retval);
  if (retcode != 0) fprintf(stderr, "join b failed %d\n", retcode);

  return 0;
}
    运行和编译说明(适用于 linux):
  • 运行命令:`sudo apt-get install build-essential`
  • 下载此代码(显然 xD)
  • 使用以下命令编译:`gcc -D_REENTRANT filenName.c -lpthread`
  • 使用命令运行结果:`./a.out`

一切正常,但我不明白为什么我的输出顺序因使用writeor不同而不同fprintf

当我使用时,write我得到如下字母的随机输出:

Starting process a
aaaaaaaaaaaaaaaaaaaaaaaaaaaaStarting process b
aaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

但是当我使用时,fprintf我总是得到类似于以下内容的输出:

Starting process a
Starting process b
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababbabaabaabaababbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

在这种情况下,文本“Starting process”总是首先出现,并且不会与输出的其余部分混合。为什么会这样?是因为write非常快而且fprintf比较慢吗?

作为一名 C 程序员,我应该使用哪一个,为什么?

4

2 回答 2

17

write是一个系统调用:它将给定的字符直接发送到操作系统,操作系统(在理论上,并且通常在实践中)立即将它们发送到输出设备,例如屏幕或磁盘。

fprintf(以及fwrite任何带FILE *参数的东西)是一个库调用,它在发送之前缓冲或收集程序内部的数据。这允许它发送更大、更统一的数据块,从而提高效率。

您看到的write是,每次调用都会导致线程切换,因为程序等待操作系统确认写入是否成功。当一个线程等待时,另一个线程获得时间。

有了fprintf,它永远不会做如此特别的事情。它实际上只是一个用 's 填充数组的线程,a直到完成。操作系统在接收到已填充的缓冲区(通过write)之前并不明智。然后由于第一个线程没有更多的工作,它运行第二个。如果您打印更多字符,您还会看到fprintfinterleave a's 和b's 块被发送到操作系统。

至于“快”和“慢”,write在发送输出时更直接,但fprintf在几乎所有其他方式上都更快,并且是一般正确的选择(或者fwrite更类似于write)。

于 2013-02-26T00:17:58.383 回答
3

写入包装内核调用,并且它没有被缓冲。它只是将 n 个字节从缓冲区复制到文件描述符。

fprintf 是缓冲的,它也是用来进行字符串处理并用参数替换各种 %d、%s 的,因此执行起来要复杂得多,必须解析它接收到的字符串。

于 2013-02-26T00:21:38.240 回答