Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
722 views
in Technique[技术] by (71.8m points)

c - weird output when I use pthread and printf

I write a program using pthread.

Environment:windows 7 , CYGWIN_NT-6.1 i686 Cygwin , gcc (GCC) 4.5.3

The source code

#include<stdio.h>
#include<pthread.h>

void *th_func(void *p)
{
    int iLoop = 0;

    for(iLoop = 0;iLoop<100;iLoop++)
    {
        printf("Thread Thread Thread Thread
");
    }

    return;
}

int main()
{
    int iLoop = 0;
    pthread_t QueThread;

    printf("Main : Start Main
");

    printf("Main : Start Create Thread
");
    pthread_create(&QueThread,NULL,th_func,NULL);
    printf("Main : End Create Thread
");

    for(iLoop = 0;iLoop<100;iLoop++)
    {
        printf("Main Main Main Main
");
    }

    pthread_join(QueThread,NULL);

    printf("Main : End Main
");

    printf("---------------
");

    return 0;
}

When I compile the source code, there are no warnings or errors,but it's output is weird.

A part of it's output

Main : Start Main
Main : Start Create Thread
Thread Thread Thread ThreThread Thread Thread Thread
Main Main Main Main
Thread Thread Thread Thread
Main Main Main Main

I want to know the cause of such phenomenon.

In this output, Main : End Create Thread is not printed completely. And at line 3, a newline at the end of "Thread Thread Thread Thread " disappear.

Is everyone's output like this? It does not occur every time, but occurs sometime.

If I use mutex to call printf safely,the weird output seem to be stopped.

POSIX says printf is thread-safe, and according to Cygwin.com, cygwin provides posix-style API. However, there is the unexpected output.

Is printf really thread-safe?

I executed the same program 100 times in Linux(Ubuntu), and this output did not occur.

In addition, I have not understood the reason why some words on the output disappeared.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The POSIX standard has functions like putc_unlocked() where the commentary says:

Versions of the functions getc(), getchar(), putc(), and putchar() respectively named getc_unlocked(), getchar_unlocked(), putc_unlocked(), and putchar_unlocked() shall be provided which are functionally equivalent to the original versions, with the exception that they are not required to be implemented in a thread-safe manner. They may only safely be used within a scope protected by flockfile() (or ftrylockfile()) and funlockfile(). These functions may safely be used in a multi-threaded program if and only if they are called while the invoking thread owns the (FILE *) object, as is the case after a successful call to the flockfile() or ftrylockfile() functions.

That clearly indicates that the low-level functions for single character I/O are normally thread-safe. However, it also indicates that the level of granularity is a single character output operation. The specification for printf() says:

Characters generated by fprintf() and printf() are printed as if fputc() had been called.

And for putc(), it says:

The putc() function shall be equivalent to fputc(), except that if it is implemented as a macro it may evaluate stream more than once, so the argument should never be an expression with side-effects.

The page for fputc() doesn't say anything about thread-safety, so you have to look elsewhere for that information.

Another section describes threads and says:

All functions defined by this volume of POSIX.1-2008 shall be thread-safe, except that the following functions need not be thread-safe.

And the list following includes the *_unlocked() functions.

So, printf() and fputc() have to be thread-safe, but the writing by printf() is done 'as if' by fputc(), so the interleaving of output between threads may be at the character level, which is more or less consistent with what you see. If you want to make calls to printf() non-interleaved, you would need to use the flockfile() and funlockfile() calls to give your thread ownership of stdout while the printf() is executed. Similarly for fprintf(). You could write an fprintf_locked() function quite easily to achieve this result:

int fprintf_locked(FILE *fp, const char *format, ...)
{
    flockfile(fp);
    va_list args;
    va_start(args, format);
    int rc = vfprintf(fp, format, args);
    va_end(args);
    funlockfile(fp);
    return rc;
}

You could insert a fflush(fp) in there if you wished. You could also have a vfprintf_locked() and have the function above call that to do the lock, format, (flush) and unlock operations. It's probably how I'd code it, trusting the compiler to inline the code if that was appropriate and doable. Supporting the versions using stdout is likewise pretty straight-forward.

Note the fragment of POSIX specification for flockfile() quoted by Michael Burr in his answer:

All functions that reference (FILE *) objects, except those with names ending in _unlocked, shall behave as if they use flockfile() and funlockfile() internally to obtain ownership of these (FILE *) objects.

Apart from the odd parentheses around the FILE *, these lines impact all the other standard I/O functions, but you have to know that these lines exist in one of the less frequently used man pages. Thus, my fprintf_locked() function should be unnecessary. If you find an aberrant implementation of fprintf() that does not lock the file, then the fprintf_locked() function could be used instead, but it should only be done under protest — the library should be doing that for you anyway.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...