我正在实现一个多线程程序,使用不同的核心,许多线程同时执行。每个线程都会调用printf()
,结果不易读取。
如何使printf()
原子化,以便一个线程中的printf()
调用不会与另一个线程中的printf()
调用冲突?
我正在实现一个多线程程序,使用不同的核心,许多线程同时执行。每个线程都会调用printf()
,结果不易读取。
如何使printf()
原子化,以便一个线程中的printf()
调用不会与另一个线程中的printf()
调用冲突?
POSIX规范包括以下函数:
getc_unlocked()
(获取字符)getchar_unlocked()
(获取标准输入流中的字符)putc_unlocked()
(输出字符)putchar_unlocked()
(向标准输出流中写入一个字符)getc()
、getchar()
、putc()
和putchar()
的版本,分别命名为getc_unlocked()
、getchar_unlocked()
、putc_unlocked()
和putchar_unlocked()
。它们在功能上与原始版本等效,但不要求以完全线程安全的方式实现。只有在受到flockfile()
(或ftrylockfile()
)和funlockfile()
保护的范围内使用时,它们才是线程安全的。如果且仅当调用线程拥有(FILE *
)对象并且在成功调用flockfile()
或ftrylockfile()
函数后的情况下,这些函数才能在多线程程序中安全地使用。
这些函数的规范提到:
flockfile()
- 获取与文件关联的锁定ftrylockfile()
- 尝试获取与文件关联的锁定,如果无法获取则立即返回funlockfile()
- 释放与文件关联的锁定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()
返回之前使用它。
...由于不再相关,因此删除了先前版本答案中的代码...
flockfile()
规范?我相信它在回答中已经非常明确地说明了,正如回答中引用的话所说:“所有涉及FILE *
对象的函数...应该表现得像它们内部使用flockfile()
和funlockfile()
一样,以获取这些(FILE *
)对象的所有权。” 我承认这是一个相当不易察觉的位置,但这就是POSIX标准中“例如printf()
这样的用户级调用将受到有效保护”的要求。 - Jonathan Leffler为了避免混淆来自不同线程的输出,您需要确保一次只有一个线程使用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);
}
对于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;
}
}
printf
的包装函数并使用互斥锁如何? - ccKepprintf
在你所说的意义上是“原子性”的,但它无法阻止多个线程写入相同的输出目标。 - Duckprintf( "xyz [%s]: ", level ); printf( fmt, args );
- 这会导致多线程问题,需要显式锁定才能解决。 - zeroimpl