如何在多个线程中使用printf()

时间:2021-11-11 00:42:32

I am implementing a multithreaded program that uses different cores, and many threads are executed simultaneously. Each thread makes a printf() call, and the result is not readable.

我正在实现一个使用不同内核的多线程程序,许多线程同时执行。每个线程都生成一个printf()调用,结果是不可读的。

How can I make printf() atomic, so that a printf() call in one thread doesn't conflict with a printf() call in another?

如何使printf()是原子的,以便一个线程中的printf()调用不会与另一个线程中的printf()调用冲突?

3 个解决方案

#1


13  

POSIX Specifications

The POSIX specification includes these functions:

POSIX规范包括以下功能:

  • getc_unlocked()
  • getc_unlocked()
  • getchar_unlocked()
  • getchar_unlocked()
  • putc_unlocked()
  • putc_unlocked()
  • putchar_unlock()
  • putchar_unlock()

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 fully thread-safe manner. They shall be thread-safe when used within a scope protected by flockfile() (or ftrylockfile()) and funlockfile(). These functions can 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.

函数的版本getc()、getchar()、putc()和putchar()分别命名为getc_解锁()、getchar_解锁()、putc_解锁()和putchar_lock(),它们在功能上等同于原始版本,但不需要以完全线程安全的方式实现它们。在受flockfile()(或ftrylockfile())和funlockfile()保护的范围内使用时,它们应该是线程安全的。如果且仅当调用线程拥有(FILE *)对象时调用这些函数,则可以安全地在多线程程序中使用这些函数,就像成功调用flockfile()或ftrylockfile()函数之后的情况一样。

The specification for these functions mention:

这些功能的说明提到:

  • flockfile()
  • flockfile()
  • ftrylockfile()
  • ftrylockfile()
  • funlockfile()
  • funlockfile()

The specification for flockfile() et al includes the blanket requirement:

flockfile()等的规范包括毛毯要求:

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.

所有引用(FILE *)对象的函数,除了名称以_unlock结尾的函数,都应该表现为在内部使用flockfile()和funlockfile()来获得这些(FILE *)对象的所有权。

This supersedes the suggested code in previous editions of this answer. The POSIX standard also specifies:

这取代了之前版本中建议的代码。POSIX标准还规定:

The [*lockfile()] functions shall behave as if there is a lock count associated with each (FILE *) object. This count is implicitly initialized to zero when the (FILE *) object is created. The (FILE *) object is unlocked when the count is zero. When the count is positive, a single thread owns the (FILE *) object. When the flockfile() function is called, if the count is zero or if the count is positive and the caller owns the (FILE *) object, the count shall be incremented. Otherwise, the calling thread shall be suspended, waiting for the count to return to zero. Each call to funlockfile() shall decrement the count. This allows matching calls to flockfile() (or successful calls to ftrylockfile()) and funlockfile() to be nested.

[*lockfile()]函数的行为应该类似于每个(FILE *)对象都有一个锁计数。在创建(FILE *)对象时,该计数被隐式初始化为零。(FILE *)对象在计数为0时解锁。当计数为正数时,一个线程拥有(FILE *)对象。当调用flockfile()函数时,如果计数为零,或者计数为正数,调用者拥有(FILE *)对象,那么计数将增加。否则,调用线程将被挂起,等待计数返回到零。对funlockfile()的每次调用都要减少计数。这允许对flockfile()的匹配调用(或对ftrylockfile()的成功调用)和funlockfile()进行嵌套。

There are also the specifications for the character I/O functions:

还有字符I/O函数的规范:

The formatted output functions are documented here:

格式化的输出函数在这里被记录:

One key provision in the printf() specification is:

printf()规范中的一个关键条款是:

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

由fprintf()和printf()生成的字符被打印为fputc()被调用。

Note the use of 'as if'. However, each of the printf() functions is required to apply the lock so that access to a stream is controlled in a multi-threaded application. Only one thread at a time can be using a given file stream. If the operations are user-level calls to fputc(), then other threads can intersperse the output. If the operations are user-level calls such as printf(), then the whole call and all access to the file stream is effectively protected so that only one thread is using it until the call to printf() returns.

注意“as if”的用法。但是,每个printf()函数都需要应用锁,以便在多线程应用程序中控制对流的访问。每次只有一个线程可以使用给定的文件流。如果操作是对fputc()的用户级调用,那么其他线程可以穿插输出。如果操作是用户级调用,比如printf(),那么整个调用和对文件流的所有访问都得到有效保护,只有一个线程在使用它,直到调用printf()返回。

In the section of the System Interfaces: General Information section of POSIX on the subject of Threads, it says:

在系统接口部分:POSIX关于线程主题的通用信息部分中,它说:

2.9.1 Thread-Safety

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

POSIX.1-2008卷定义的所有函数都应该是线程安全的,但是以下函数s1不需要是线程安全的。

…a list of functions that need not be thread-safe…

一个不需要线程安全的函数列表…

… The getc_unlocked(), getchar_unlocked(), putc_unlocked(), and putchar_unlocked() functions need not be thread-safe unless the invoking thread owns the (FILE *) object accessed by the call, as is the case after a successful call to the flockfile() or ftrylockfile() functions.

…getc_unlock()、getchar_unlock()、putc_unlock()和putchar_unlock()函数不需要是线程安全的,除非调用线程拥有调用所访问的(FILE *)对象,就像调用flockfile()函数或ftrylockfile()函数之后的情况一样。

Implementations shall provide internal synchronization as necessary in order to satisfy this requirement.

实现应提供必要的内部同步,以满足此需求。

The list of exempted functions does not contain fputc or putc or putchar (or printf() et al).

豁免的功能清单不包含fputc或putc或putchar(或printf()等)。

Interpretation

Rewritten 2017-07-26.

重写2017-07-26。

  1. Character-level output on a stream is thread-safe unless using the 'unlocked' functions without first locking the file.
  2. 流上的字符级输出是线程安全的,除非使用“解锁”函数而不首先锁定文件。
  3. Higher-level functions such as printf() conceptually call flockfile() at the start an funlockfile() at the end, which means that the POSIX-defined stream output functions are also thread-safe per call.
  4. 更高级别的函数,如printf(),概念上在末尾调用funlockfile()时调用flockfile(),这意味着posix定义的流输出函数在每次调用时也是线程安全的。
  5. If you wish to group operations on a file stream for a single thread, you can do so by explicitly using calls to flockfile() and funlockfile() on the relevant stream (without interfering with the system's use of the *lockfile() functions.
  6. 如果希望为单个线程对文件流进行分组操作,可以通过在相关流上显式地使用对flockfile()和funlockfile()的调用(不影响系统对*lockfile()函数的使用)来实现。

This means there is no need to create mutexes or equivalent mechanisms for yourself; the implementation provides the functions to allow you to control the access to printf() et al in a multi-threaded application.

这意味着不需要为自己创建互斥体或等效机制;该实现提供了允许您在多线程应用程序中控制对printf()等的访问的函数。

…Code from previous answer removed as no longer relevant…

从先前答案中删除的代码不再相关……

#2


11  

In order not to mix the outputs from different threads, you need to make sure that only one thread uses printf at a time. To achieve this, the simplest solution is to use a mutex. At the beginning initialize the mutex :

为了不混合来自不同线程的输出,您需要确保每次只有一个线程使用printf。要实现这一点,最简单的解决方案是使用互斥体。在开始初始化互斥量:

static pthread_mutex_t printf_mutex;
...
int main()
{
    ...
    pthread_mutex_init(&printf_mutex, NULL);
    ...

Then make a wrapper around printf to make sure that only the thread that got the mutex can call printf (otherwise it will have to block until the mutex is available) :

然后在printf周围做一个包装,以确保只有获得互斥对象的线程可以调用printf(否则它必须阻塞,直到互斥对象可用):

int sync_printf(const char *format, ...)
{
    va_list args;
    va_start(args, format);

    pthread_mutex_lock(&printf_mutex);
    vprintf(format, args);
    pthread_mutex_unlock(&printf_mutex);

    va_end(args);
}

#3


-2  

For linux ,here's the code for u in c:3 threads ,executing on different cores printing hello world ,not conflicting with each other courtisey of lock .

对于linux,这是c:3个线程中的u代码,在不同的内核上执行,打印hello world,不会互相冲突。

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <syscall.h>
#include <sys/types.h>

void * printA ( void *);
void * printB ( void *);
void * printC ( void *);

pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;


int main(int argc,  char *argv[]) {
   int error;
   pthread_t tid1, tid2,tid3;

    if ( error = pthread_create (&tid1, NULL, printA, NULL ))
        {
        fprintf (stderr, "Failed to create first thread: %s\n",strerror(error));
        return 1;
    }
    if ( error = pthread_create (&tid2, NULL, printB, NULL ))
        {
        fprintf (stderr, "Failed to create second thread: %s\n",strerror(error));
        return 1;
    }
    if ( error = pthread_create (&tid3, NULL, printC, NULL ))
        {
        fprintf (stderr, "Failed to create second thread: %s\n",strerror(error));
        return 1;
    }

    if (error = pthread_join(tid1, NULL))
        {
        fprintf (stderr, "Failed to join first thread: %s\n",strerror(error));
        return 1;
    }
    if (error = pthread_join(tid2, NULL))
        {
        fprintf (stderr, "Failed to join second thread: %s\n",strerror(error));
        return 1;
    }

    if (error = pthread_join(tid3, NULL))
        {
        fprintf (stderr, "Failed to join second thread: %s\n",strerror(error));
        return 1;
    }
    return 0;
}

void * printA ( void *arg )
{
      if ( error = pthread_mutex_lock( &mylock ))
      {
    fprintf (stderr, "Failed to acquire lock in printA: %s\n",strerror(error));
    return NULL;
      }
   printf("Hello world\n");

      if ( error = pthread_mutex_unlock( &mylock ))
      {
    fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error));
    return NULL;
      }
   }

void * printB ( void *arg )
{
   int error;
      if ( error = pthread_mutex_lock( &mylock ))
      {
    fprintf (stderr, "Failed to acquire lock in printB: %s\n",strerror(error));
    return NULL;
      }


   printf("Hello world\n");

      if ( error = pthread_mutex_unlock( &mylock ))
      {
    fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error));
    return NULL;
      }
   }


void * printC ( void *arg )
{
   int error;
      if ( error = pthread_mutex_lock( &mylock ))
      {
    fprintf (stderr, "Failed to acquire lock in printB: %s\n",strerror(error));
    return NULL;
      }


   printf("Hello world\n");

      if ( error = pthread_mutex_unlock( &mylock ))
      {
    fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error));
    return NULL;
      }
   }

#1


13  

POSIX Specifications

The POSIX specification includes these functions:

POSIX规范包括以下功能:

  • getc_unlocked()
  • getc_unlocked()
  • getchar_unlocked()
  • getchar_unlocked()
  • putc_unlocked()
  • putc_unlocked()
  • putchar_unlock()
  • putchar_unlock()

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 fully thread-safe manner. They shall be thread-safe when used within a scope protected by flockfile() (or ftrylockfile()) and funlockfile(). These functions can 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.

函数的版本getc()、getchar()、putc()和putchar()分别命名为getc_解锁()、getchar_解锁()、putc_解锁()和putchar_lock(),它们在功能上等同于原始版本,但不需要以完全线程安全的方式实现它们。在受flockfile()(或ftrylockfile())和funlockfile()保护的范围内使用时,它们应该是线程安全的。如果且仅当调用线程拥有(FILE *)对象时调用这些函数,则可以安全地在多线程程序中使用这些函数,就像成功调用flockfile()或ftrylockfile()函数之后的情况一样。

The specification for these functions mention:

这些功能的说明提到:

  • flockfile()
  • flockfile()
  • ftrylockfile()
  • ftrylockfile()
  • funlockfile()
  • funlockfile()

The specification for flockfile() et al includes the blanket requirement:

flockfile()等的规范包括毛毯要求:

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.

所有引用(FILE *)对象的函数,除了名称以_unlock结尾的函数,都应该表现为在内部使用flockfile()和funlockfile()来获得这些(FILE *)对象的所有权。

This supersedes the suggested code in previous editions of this answer. The POSIX standard also specifies:

这取代了之前版本中建议的代码。POSIX标准还规定:

The [*lockfile()] functions shall behave as if there is a lock count associated with each (FILE *) object. This count is implicitly initialized to zero when the (FILE *) object is created. The (FILE *) object is unlocked when the count is zero. When the count is positive, a single thread owns the (FILE *) object. When the flockfile() function is called, if the count is zero or if the count is positive and the caller owns the (FILE *) object, the count shall be incremented. Otherwise, the calling thread shall be suspended, waiting for the count to return to zero. Each call to funlockfile() shall decrement the count. This allows matching calls to flockfile() (or successful calls to ftrylockfile()) and funlockfile() to be nested.

[*lockfile()]函数的行为应该类似于每个(FILE *)对象都有一个锁计数。在创建(FILE *)对象时,该计数被隐式初始化为零。(FILE *)对象在计数为0时解锁。当计数为正数时,一个线程拥有(FILE *)对象。当调用flockfile()函数时,如果计数为零,或者计数为正数,调用者拥有(FILE *)对象,那么计数将增加。否则,调用线程将被挂起,等待计数返回到零。对funlockfile()的每次调用都要减少计数。这允许对flockfile()的匹配调用(或对ftrylockfile()的成功调用)和funlockfile()进行嵌套。

There are also the specifications for the character I/O functions:

还有字符I/O函数的规范:

The formatted output functions are documented here:

格式化的输出函数在这里被记录:

One key provision in the printf() specification is:

printf()规范中的一个关键条款是:

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

由fprintf()和printf()生成的字符被打印为fputc()被调用。

Note the use of 'as if'. However, each of the printf() functions is required to apply the lock so that access to a stream is controlled in a multi-threaded application. Only one thread at a time can be using a given file stream. If the operations are user-level calls to fputc(), then other threads can intersperse the output. If the operations are user-level calls such as printf(), then the whole call and all access to the file stream is effectively protected so that only one thread is using it until the call to printf() returns.

注意“as if”的用法。但是,每个printf()函数都需要应用锁,以便在多线程应用程序中控制对流的访问。每次只有一个线程可以使用给定的文件流。如果操作是对fputc()的用户级调用,那么其他线程可以穿插输出。如果操作是用户级调用,比如printf(),那么整个调用和对文件流的所有访问都得到有效保护,只有一个线程在使用它,直到调用printf()返回。

In the section of the System Interfaces: General Information section of POSIX on the subject of Threads, it says:

在系统接口部分:POSIX关于线程主题的通用信息部分中,它说:

2.9.1 Thread-Safety

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

POSIX.1-2008卷定义的所有函数都应该是线程安全的,但是以下函数s1不需要是线程安全的。

…a list of functions that need not be thread-safe…

一个不需要线程安全的函数列表…

… The getc_unlocked(), getchar_unlocked(), putc_unlocked(), and putchar_unlocked() functions need not be thread-safe unless the invoking thread owns the (FILE *) object accessed by the call, as is the case after a successful call to the flockfile() or ftrylockfile() functions.

…getc_unlock()、getchar_unlock()、putc_unlock()和putchar_unlock()函数不需要是线程安全的,除非调用线程拥有调用所访问的(FILE *)对象,就像调用flockfile()函数或ftrylockfile()函数之后的情况一样。

Implementations shall provide internal synchronization as necessary in order to satisfy this requirement.

实现应提供必要的内部同步,以满足此需求。

The list of exempted functions does not contain fputc or putc or putchar (or printf() et al).

豁免的功能清单不包含fputc或putc或putchar(或printf()等)。

Interpretation

Rewritten 2017-07-26.

重写2017-07-26。

  1. Character-level output on a stream is thread-safe unless using the 'unlocked' functions without first locking the file.
  2. 流上的字符级输出是线程安全的,除非使用“解锁”函数而不首先锁定文件。
  3. Higher-level functions such as printf() conceptually call flockfile() at the start an funlockfile() at the end, which means that the POSIX-defined stream output functions are also thread-safe per call.
  4. 更高级别的函数,如printf(),概念上在末尾调用funlockfile()时调用flockfile(),这意味着posix定义的流输出函数在每次调用时也是线程安全的。
  5. If you wish to group operations on a file stream for a single thread, you can do so by explicitly using calls to flockfile() and funlockfile() on the relevant stream (without interfering with the system's use of the *lockfile() functions.
  6. 如果希望为单个线程对文件流进行分组操作,可以通过在相关流上显式地使用对flockfile()和funlockfile()的调用(不影响系统对*lockfile()函数的使用)来实现。

This means there is no need to create mutexes or equivalent mechanisms for yourself; the implementation provides the functions to allow you to control the access to printf() et al in a multi-threaded application.

这意味着不需要为自己创建互斥体或等效机制;该实现提供了允许您在多线程应用程序中控制对printf()等的访问的函数。

…Code from previous answer removed as no longer relevant…

从先前答案中删除的代码不再相关……

#2


11  

In order not to mix the outputs from different threads, you need to make sure that only one thread uses printf at a time. To achieve this, the simplest solution is to use a mutex. At the beginning initialize the mutex :

为了不混合来自不同线程的输出,您需要确保每次只有一个线程使用printf。要实现这一点,最简单的解决方案是使用互斥体。在开始初始化互斥量:

static pthread_mutex_t printf_mutex;
...
int main()
{
    ...
    pthread_mutex_init(&printf_mutex, NULL);
    ...

Then make a wrapper around printf to make sure that only the thread that got the mutex can call printf (otherwise it will have to block until the mutex is available) :

然后在printf周围做一个包装,以确保只有获得互斥对象的线程可以调用printf(否则它必须阻塞,直到互斥对象可用):

int sync_printf(const char *format, ...)
{
    va_list args;
    va_start(args, format);

    pthread_mutex_lock(&printf_mutex);
    vprintf(format, args);
    pthread_mutex_unlock(&printf_mutex);

    va_end(args);
}

#3


-2  

For linux ,here's the code for u in c:3 threads ,executing on different cores printing hello world ,not conflicting with each other courtisey of lock .

对于linux,这是c:3个线程中的u代码,在不同的内核上执行,打印hello world,不会互相冲突。

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <syscall.h>
#include <sys/types.h>

void * printA ( void *);
void * printB ( void *);
void * printC ( void *);

pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;


int main(int argc,  char *argv[]) {
   int error;
   pthread_t tid1, tid2,tid3;

    if ( error = pthread_create (&tid1, NULL, printA, NULL ))
        {
        fprintf (stderr, "Failed to create first thread: %s\n",strerror(error));
        return 1;
    }
    if ( error = pthread_create (&tid2, NULL, printB, NULL ))
        {
        fprintf (stderr, "Failed to create second thread: %s\n",strerror(error));
        return 1;
    }
    if ( error = pthread_create (&tid3, NULL, printC, NULL ))
        {
        fprintf (stderr, "Failed to create second thread: %s\n",strerror(error));
        return 1;
    }

    if (error = pthread_join(tid1, NULL))
        {
        fprintf (stderr, "Failed to join first thread: %s\n",strerror(error));
        return 1;
    }
    if (error = pthread_join(tid2, NULL))
        {
        fprintf (stderr, "Failed to join second thread: %s\n",strerror(error));
        return 1;
    }

    if (error = pthread_join(tid3, NULL))
        {
        fprintf (stderr, "Failed to join second thread: %s\n",strerror(error));
        return 1;
    }
    return 0;
}

void * printA ( void *arg )
{
      if ( error = pthread_mutex_lock( &mylock ))
      {
    fprintf (stderr, "Failed to acquire lock in printA: %s\n",strerror(error));
    return NULL;
      }
   printf("Hello world\n");

      if ( error = pthread_mutex_unlock( &mylock ))
      {
    fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error));
    return NULL;
      }
   }

void * printB ( void *arg )
{
   int error;
      if ( error = pthread_mutex_lock( &mylock ))
      {
    fprintf (stderr, "Failed to acquire lock in printB: %s\n",strerror(error));
    return NULL;
      }


   printf("Hello world\n");

      if ( error = pthread_mutex_unlock( &mylock ))
      {
    fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error));
    return NULL;
      }
   }


void * printC ( void *arg )
{
   int error;
      if ( error = pthread_mutex_lock( &mylock ))
      {
    fprintf (stderr, "Failed to acquire lock in printB: %s\n",strerror(error));
    return NULL;
      }


   printf("Hello world\n");

      if ( error = pthread_mutex_unlock( &mylock ))
      {
    fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error));
    return NULL;
      }
   }