I have recently started learning the wonders of pthreads according to POSIX 1003.1c.

PThreads may seem complex, but they are basically simple threads that we use in the class to create parallel behavior: https://computing.llnl.gov/tutorials/pthreads/

As I am still learning, my teacher gave us a C code to toy with:

/* 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;
}
    Instructions to run and compile (for linux):
  • Run command: `sudo apt-get install build-essential`
  • Donwload this code (obviously xD)
  • Use the following command to compile: `gcc -D_REENTRANT filenName.c -lpthread`
  • Run the result by using the command: `./a.out`

Everything works but I don't understand why my output order is different depending on the use of write or fprintf.

When I use write I get a random output of letters like the following:

Starting process a
aaaaaaaaaaaaaaaaaaaaaaaaaaaaStarting process b
aaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

But when I use fprintf I always get the get an output similar to:

Starting process a
Starting process b
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababbabaabaabaababbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

In this case, the text "Starting process" always appears first and is not mixed with the rest of the output. Why is this happening? Is it because write is very fast and fprintf is slower?

As a C programmer, which one should I use and why?

up vote 11 down vote accepted

write is a system call: it sends the given characters directly to the operating system, which (in theory, and often in practice) sends them immediately to the output device such as the screen or disk.

fprintf (and fwrite and anything that takes a FILE * argument) is a library call, which buffers, or collects, the data inside your program before sending it. This allows it to send larger, more uniform chunks of data, which improves efficiency.

What you see with write is that each call causes a thread switch as the program waits for the operating system to confirm whether the write succeeded. When one thread is waiting, the other thread gets time.

With fprintf, it never does anything so exceptional. It's actually just one thread filling an array with a's until it's done. The operating system is none the wiser until it receives the filled buffer (via write). Then since the first thread has no more work, it runs the second. If you printed more characters, you would see fprintf also interleave a's and b's as the chunks get sent to the operating system.

As for "fast" and "slow", write is more immediate in sending its output, but fprintf is faster in pretty much every other way and is the general correct choice (or fwrite which is more similar to write).

Write wraps a kernel call, and it's not buffered. It just copies n bytes from the buffer to the file descriptor.

fprintf is buffered and also it is meant to do string processing and replace the various %d, %s with the parameters, so it is much more complex to execute for this reason, having to parse the string that it receives.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.