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

时间:2014-05-10 21:47:56

标签: c linux multithreading printf multiprocess

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

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

3 个答案:

答案 0 :(得分:19)

POSIX规范

POSIX规范包括以下功能:

  

分别命名为getc()getchar()putc()的函数putchar()getc_unlocked()getchar_unlocked()putc_unlocked()的版本,并且应提供putchar_unlocked(),它们在功能上等同于原始版本,但不要求它们以完全线程安全的方式实现。在受flockfile()(或ftrylockfile())和funlockfile()保护的范围内使用时,它们应是线程安全的。当且仅当在调用线程拥有(FILE *)对象时调用它们时,可以安全地在多线程程序中使用这些函数,就像成功调用flockfile()ftrylockfile()函数。

这些功能的规范提到:

flockfile()等的规范包括一揽子要求:

  

所有引用(FILE *)对象的函数,除了名称以_unlocked结尾的对象外,其行为应该像在内部使用flockfile()funlockfile()一样获得所有权这些(FILE *)个对象。

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

  

[*lockfile()]函数的行为就像存在与每个(FILE *)对象关联的锁定计数一样。创建(FILE *)对象时,此计数隐式初始化为零。当计数为零时,(FILE *)对象被解锁。当计数为正时,单个线程拥有(FILE *)对象。调用flockfile()函数时,如果计数为零或计数为正且调用者拥有(FILE *)对象,则计数应递增。否则,调用线程将被挂起,等待计数返回零。每次调用funlockfile()都会减少计数。这样就可以将与flockfile()(或成功调用ftrylockfile())和funlockfile()的来电匹配。

还有字符I / O功能的规范:

格式化输出功能在此处记录:

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

  

fprintf()printf()生成的字符将被打印,就像调用了fputc()一样。

请注意使用',就像'一样。但是,每个printf()函数都需要应用锁,以便在多线程应用程序中控制对流的访问。一次只有一个线程可以使用给定的文件流。如果操作是对fputc()的用户级调用,则其他线程可以散布输出。如果操作是用户级调用,例如printf(),则整个调用和对文件流的所有访问都得到有效保护,因此只有一个线程正在使用它,直到调用printf()为止。< / p>

在关于Threads主题的POSIX的系统界面:一般信息部分中,它说:

  

2.9.1线程安全

     

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

     

...一系列不需要线程安全的函数...

     

...除非调用线程拥有(getc_unlocked())对象,否则getchar_unlocked()putc_unlocked()putchar_unlocked()FILE *函数不一定是线程安全的通过电话访问,就像成功调用flockfile()ftrylockfile()函数后的情况一样。

     

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

豁免功能列表不包含fputcputcputchar(或printf()等)。

解释

改写2017-07-26。

  1. 流上的字符级输出是线程安全的,除非在没有先锁定文件的情况下使用'unlocked'函数。
  2. 诸如printf()之类的高级函数在结尾处从概念上调用flockfile() funlockfile(),这意味着POSIX定义的流输出函数也是线程安全的调用
  3. 如果您希望对单个线程的文件流进行操作分组,则可以通过在相关流上明确使用对flockfile()funlockfile()的调用来实现此操作(不会干扰系统的使用*lockfile()函数。
  4. 这意味着您无需为自己创建互斥锁或等效机制;该实现提供的功能允许您在多线程应用程序中控制对printf()等的访问。

    ...之前回答的代码已删除,因为不再相关......

答案 1 :(得分:15)

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

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

然后围绕printf创建一个包装器,以确保只有获得mutex的线程可以调用printf(否则它必须阻塞,直到mutex为可用):

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);
}

答案 2 :(得分:-2)

对于linux,这里是你在c:3个线程中的代码,在不同的核心上执行打印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;
      }
   }